'01.JAVA/Java'에 해당되는 글 199건

  1. 2013.12.23 [펌] 객체 직렬화란?
  2. 2013.09.13 JSONUtil
  3. 2013.08.06 Request.getParameterMap values not castable to string
  4. 2013.08.06 [펌] [java] for 문으로 Map loop돌때 쓰면 좋은 방법.
  5. 2013.02.18 [펌] Java development 2.0: Hadoop MapReduce로 대용량 데이터 분석
  6. 2012.12.06 java doc 만들때 주석
  7. 2012.11.14 eclipse에서 junit 실행시 OutOfMemory
  8. 2012.10.17 [펌] How to sort a Map<Key, Value> on the values in Java?
  9. 2012.09.21 java 썸네일 만들기
  10. 2012.09.21 간단한 text 파일 만들기
  11. 2012.09.21 java d-day
  12. 2012.09.21 split과 StringTokenizer 차이
  13. 2012.09.21 간단한 text 파일 만들기
  14. 2012.09.20 eclipse assertion 사용해 보기
  15. 2012.09.20 Integrate Calculations Defined In Spreadsheets Into Java Apps
  16. 2012.09.20 엑셀 함수 자바로 구현
  17. 2012.09.15 실행되는 함수내에서 Method명과 Class명 알아내기
  18. 2012.09.15 JAVA에서 쉘스크립트 실행
  19. 2012.05.18 Java 개발자를 위한 Dojo 개념
  20. 2012.05.08 Java jstat로 메모리 모니터링
  21. 2012.05.08 Jstat 으로 JVM heap memory 모니터링 하기
  22. 2012.05.08 JVM을 사용하여 디렉토리 프록시 서버에 대한 모니터링된 데이터 검색
  23. 2012.05.08 Java SE 6 플랫폼 응용 프로그램 모니터링 및 관리
  24. 2012.01.27 jar 파일 생성 시 MANIFEST.MF 작성
  25. 2011.10.29 serialVersionUID 기본 알고리즘
  26. 2011.07.15 [XSS 취약점 보완] Cross Site Scripting 방지 기법 - Commons Lang
  27. 2011.07.11 jsp, java 한글 깨짐현상
  28. 2011.02.22 숫자를 한글로 반환
  29. 2011.02.12 request 값 변경 1
  30. 2010.12.06 [펌] dataoutputstream.writeUTF 에러
01.JAVA/Java2013. 12. 23. 11:43
반응형

객체 직렬화란?

- 직렬화: Heap에 위치한 객체를 출력 가능한 상태로 만드는 작업

- 역직렬화: 직렬화된 객체를 다시 Heap에 넣기 위한 작업

- 직렬화의 대상은 객체의 Attribute의 값. (메소드는 그저 주소값만 필요할뿐)

 

- 객체를 IO하기 위해서는 필터스트림인 ObjectInputStream과 ObjectOutputStream이 필요

- 직렬화는 ObjectOutputStream

- 역직렬화는 ObjectInputStream

- 직렬화 대상객체는 java.io.Serializable를 implements한 클래스 객체여야 한다.

 



 

- 자바의 메모리 구조는 그림과 깉이 되있는데 여기서 보듯이 객체는 Attribute 데이터, 그리고 Method의 주소만 갖고 있을 뿐이다.

따라서 직렬화의 대상은 실체가 없는 Method는 제외하고 Attribute만 직렬화하게 된다.




객체 직렬화 하는 방법

- Serializable Interface를 implements한 class의 객체는 뭐든 직렬화가 가능하다.



ex) 직렬화 대상 클래스

 

import java.io.*; 

public class Member implements Serializable{
    private String name; 
    private transient int age;
    private String address; 
    private transient MyDate birthday;
    ...
}


ex) 객체 직렬화 하기

 

public void writeMemberObject(Member m) throws IOException {
    ObjectOutputStream oos = null; 
    try{ 
        oos = new ObjectOutputStream(new FileOutputStream("mem.obj")); // 연결 + 필터 추가 
        oos.writeObject(m); // 객체를 출력하는 메소드: writeObject(Object obj) 
        // write할 때 Exception이 발생하는데 이 때 close를 안하면 스트림이 안닫혀있는 상태로 방치 
    } finally { 
        if(oos!=null) { 
            oos.close(); // 그래서 close로 닫아줌 
        } 
    } 
}





객체 직렬화 피하기 (또는 피해야 되는 경우)

 

- 객체 Instance 변수 앞에 transient 키워드를 붙이면 직렬화 대상에서 제외된다.

- 그런데 왜 직렬화 대상에서 제외할까? 답은...


1. 보안적인 측면에서 직렬화 할 때 빼고 싶을 때

 보안 문제로 직렬화 하면 안되는 어트리뷰트가 여기에 해당한다.


2. 직렬화 대상이 아닌 객체 type일 경우

 만약 다음과 그림과 같은 A 객체와 B 객체를 직렬화 한다고 해보자.


위와 같은 구조에서 A, B 객체들을 직렬화하고 싶을 때 어떻게 할까?


답은 B 객체의 멤버 인스턴스인 C 객체에 transient 키워드를 붙여준다.

그러면 직렬화 대상에서 제외되므로 소스 코드를 통채로 고치지 않아도 된다.






역직렬화 객체를 복원하는 방법

- 객체를 만든 뒤 readObject() 메소드의 리턴하는 Attribute 대입

- 자바가 역직렬화하는 방식은 JVM이 역직렬화 하고자 하는 객체를 만들어서 리턴한다.



ex)

 

public Member readMemberObject() throws IOException, ClassNotFoundException {
    ObjectInputStream ois = null; 
    Member m = null; 
    try    { 
        ois = new ObjectInputStream(new FileInputStream("mem.obj")); 
        m = (Member)ois.readObject(); 
    } finally { 
        ois.close(); 
        return m; 
    } 
}

 



그런데 여기서 문제가 발생한다.

만약 역직렬화해서 만든 객체(readObject() 메소드로 불러들인 객체)의 클래스가 변경이 되었다면?

그런데도 역직렬화한 객체를 변경된 클래스의 인스턴스에 대입하려고 한다면?


당연히 이 때 JVM은 역직렬화 객체를 대입할 수 없다고 예외를 발생시킨다.

좀 더 자세히 살펴보면 역직렬화한 객체와 새로 변경된 클래스의 serialVersionUID가 일치하지 않아서 발생하는 문제인데

새로 변경된 클래스와 역직렬화하는 객체의 serialVerionUID를 일치시켜주면 된다.


그럼 또 어떻게 serialVerionUID를 일치시켜준단 말인가?

답은...


클래스를 작성할 때 미리 serialVerionUID를 상수로 고정시켜 놓는다. 이렇게 되면 클래스가 새로 변경이 되도 역직렬화하는 객체와 serialVerionUID가 같기 때문에 예외가 더 이상 발생하지 않는다.



ex) 직렬화 대상 클래스 (역직렬화 문제 해결)

 

import java.io.*; 

public class Member implements Serializable{
    // 직렬화, 역직렬화 시 serialVersionUID 상수가 없으면 JVM이 만들어서 넣어준다. 
    // 있으면 만들지 않는다. 
    static final long serialVersionUID = 100L;
    private String name; 
    private transient int age;
    ...
}
Posted by 1010
01.JAVA/Java2013. 9. 13. 09:34
반응형
package com.ociweb.json;

import java.util.*;
import net.sf.json.*;
import org.apache.commons.beanutils.DynaBean;
import org.apache.commons.beanutils.DynaClass;
import org.apache.commons.beanutils.DynaProperty;

import static com.ociweb.lang.SystemUtil.*;

public class JSONUtil {

  private Map<String, Class> classMap = new HashMap<String, Class>();

  /**
   * Adds a mapping that is used when converting from a JSON map
   * to a Java Bean.  Values of map entries with the given key
   * will be converted to Java Beans with the given Class.
   */
  public void addMapping(String key, Class clazz) {
    classMap.put(key, clazz);
  }

  /**
   * Converts a JSON string representing an array
   * to an array of Java Beans of a given Class.
   */
  public Object[] fromJSONArray(String json, Class clazz) {
    JSONArray ja = JSONArray.fromString(json);
    return JSONArray.toArray(ja, clazz, classMap);
  }

  /**
   * Converts a JSON string representing an array
   * to a java.util.List that contains Java Beans of a given Class.
   */
  public List fromJSONList(String json, Class clazz) {
    JSONArray ja = JSONArray.fromString(json);
    return JSONArray.toList(ja, clazz, classMap);
  }

  /**
   * Converts a JSON string representing an object
   * to a Java Bean object of a given Class.
   */
  public Object fromJSONObject(String json, Class clazz) {
    JSONObject jo = JSONObject.fromString(json);
    return JSONObject.toBean(jo, clazz, classMap);
  }

  /**
   * Pretty prints a JSONArray or JSONObject.
   */
  private void prettyPrint(JSON j) {
    out(j.toString(2));
  }

  /**
   * Pretty prints a JSON string that represents an array.
   */
  public void prettyPrintArray(String json) {
    prettyPrint(JSONArray.fromString(json));
  }

  /**
   * Pretty prints a JSON string that represents an object.
   */
  public void prettyPrintObject(String json) {
    prettyPrint(JSONObject.fromString(json));
  }

  /**
   * Converts a java.util.List to a JSON string.
   */
  public String toJSON(List list) {
    JSONArray ja = JSONArray.fromCollection(list);
    return ja.toString();
  }

  /**
   * Converts a Java Bean to a JSON string.
   */
  public String toJSON(Object object) {
    JSONObject jo = JSONObject.fromObject(object);
    return jo.toString();
  }

  /**
   * Converts an array of booleans, numbers, strings and Java Beans
   * to a JSON string.
   */
  public String toJSON(Object[] array) {
    JSONArray ja = JSONArray.fromArray(array);
    return ja.toString();
  }
}

 

Posted by 1010
01.JAVA/Java2013. 8. 6. 17:37
반응형

i am trying to get the complete parameter map from the request object and iterate over it.

here is the sample code

  Map map = request.getParameterMap();
for(Object key : map.keySet()){
    String keyStr = (String)key;
    Object value = map.get(keyStr);     
    System.out.println("Key " + (String)key + "     :    " + value);
}

output

  Key businessunit     :    [Ljava.lang.String;@388f8321
   Key site     :    [Ljava.lang.String;@55ea0889
  Key startDate     :    [Ljava.lang.String;@77d6866f
  Key submit     :    [Ljava.lang.String;@25141ee0
  Key traffictype     :    [Ljava.lang.String;@4bf71724

its evident from the output that the value object is an instance of String

now when i change my code to something like this

  Map map = request.getParameterMap();
  for(Object key : map.keySet()){
    String keyStr = (String)key;
    Object value = map.get(keyStr);
    if(value instanceof String)
    System.out.println("Key " + (String)key + "     :    " + (String)value);
}

it prints nothing but as per the previous output it should have printed the values and if i remove instanceOf check it gives ClassCastException. is this the expected behavior or i am doing something wrong here ?

share|improve this question
Three possibilities: 1. Did you redeclare class String somewhere (try using java.lang.String instead of String to find out)? 2. Are you using the exact same values when running the two pieces of code? 3. Try using String.valueOf(value) or value.toString() and see what happens. Hope it helps. – The Nail Dec 5 '11 at 7:34
Ah forget the above, it's an array. Fooled me. – The Nail Dec 5 '11 at 7:36
add comment (requires an account with 50 reputation)

[Ljava.lang.String;@XXXXXXX means it is array of String not a single String. So your condition fails and it does not print anything.

share|improve this answer
add comment (requires an account with 50 reputation)

The value is an array. If you're sure that the array is not empty, you should get the string value like this:

String value = (String) map.get(keyStr)[0];
share|improve this answer
add comment (requires an account with 50 reputation)

As the object which is returned is an array of strings as Harry Joy pointed out, you will have to use the Arrays.toString() method in order to convert that array to a printable string:

    Map map = request.getParameterMap();
    for (Object key: map.keySet())
    {
            String keyStr = (String)key;
            String[] value = (String[])map.get(keyStr);
            System.out.println("Key" + (String)key + "   :   " + Arrays.toString(value));
    }
share|improve this answer
Posted by 1010
01.JAVA/Java2013. 8. 6. 16:50
반응형

출처 : http://lotus.tistory.com/78

 

Map 같은 자료구조로 이터레이션 할때 여러가지 방법을 쓸 수 있는데 검색하다가 깔끔한 코드를 발견해서 첨부해 본다. ㅋ
난 자바 뉴비..ㅎㅎ

- 그저 그런 방법

Set<Integer> set = map.keySet();
for(Integer key : set)
{
System.out.println(key + " " + map.get(key));
}

- 좀 더 깔끔한 방법
for (Map.Entry<Integer, String> entry : map.entrySet() )
{
System.out.println(entry.getKey() + " " + entry.getValue());
}


아래의 주소에서 소스코드를 참조했음
http://jtoee.blogspot.com/2008/05/better-way-to-iterate-java-maps.html 
Posted by 1010
01.JAVA/Java2013. 2. 18. 10:39
반응형

출처 : http://www.ibm.com/developerworks/kr/library/j-javadev2-15/

Google이 2001년에 이미지 검색 기능을 실행했을 때, 2억 5천만 개의 인덱스된 이미지를 보유했다. 십 년이 지나지 않아, 이 검색 거인은 100억 개 이상의 이미지를 인덱스했다. 분당 35시간의 컨텐츠가 YouTube로 업로드된다. Twitter는 평균적으로 매일 5천 5백만 건의 트윗을 처리한다고 주장한다. 올해 초에 그 검색 기능은 매일 6억 개의 쿼리를 로깅했다. 대용량 데이터를 논의할 때 대용량이란 바로 그 정도이다.

이 시리즈의 정보

처음 Java 기술이 발표된 이후로 Java를 개발하는 과정은 급속도로 변화되었다. 오픈 소스 프레임워크와 신뢰할 수 있는 임대용 전개 인프라 덕택에 Java 애플리케이션을 신속하고 저렴하게 어셈블하고 테스트하고 유지할 수 있게 되었다. 이 시리즈에서 Andrew Glover는 이러한 새로운 Java 개발 패러다임을 가능하게 하는 다양한 기술과 도구를 탐구한다.

이러한 엄청난 규모의 데이터는 대기업, 대학 및 정부 — 엄청나게 비싼 슈퍼컴퓨터를 구입할 수 있으며 이를 계속 실행할 직원을 가용할 수 있는 단체 —에 국한되었다. 오늘날 스토리지 비용의 절감과 프로세싱 성능의 상용화를 통해 중소기업 및 일부 개인들도 동일한 데이터를 저장하고 마이닝하기 시작하여, 애플리케이션 혁신의 흐름을 발전시킨다.

대용량 데이터 혁명이 가능한 기술 중 하나는 MapReduce이다. 이는 엄청난 규모의 분산 데이터 세트를 처리하기 위해 Google이 개발한 프로그래밍 모델 겸 구현 방식이다. 이 기사에서 필자는 Apache의 오픈 소스 MapReduce 구현 방식인 Hadoop을 소개한다. 이는 클라우드 컴퓨팅의 킬러 앱(killer app)이라고도 한다.

Hadoop 정보

Apache의 Hadoop 프레임워크는 본질적으로 대용량 데이터 세트를 분석하기 위한 메커니즘이며, 이는 반드시 데이터 저장소에 수용하지 않아도 된다. Hadoop은 MapReduce의 거대한 데이터 분석 엔진을 요약하여 개발자들이 액세스하기에 더 쉽게 만든다. Hadoop은 무수한 노드로 확장하여 데이터 정렬과 관련된 활동 및 조정을 모두 처리할 수 있다.

Hadoop은 수많은 기능과 구성을 통해 놀랍도록 유용하고 강력한 프레임워크가 된다. Yahoo!와 무수한 다른 조직들은 산더미 같은 비트와 바이트들을 분석하기 위해 이를 효율적인 메커니즘이라고 인식했다. 또한 Hadoop은 단일 노드에서 작업하기에 매우 간편하다. 즉, 분석할 데이터가 어느 정도 있고, 제네릭을 비롯한 Java 코드에 익숙하기만 하면 된다. Hadoop은 또한 Ruby, Python 및 C++로 작업한다.

MapReduce에 대한 추가 정보

이 시리즈의 독자라면 이미 MapReduce가 작동하는 것을 두 세 번 확인했다. "Java development 2.0: CouchDB와 그루비의 RESTClient를 이용한 REST"에서 필자는 CouchDB가 뷰를 위해 MapReduce를 어떻게 활용하는지 시연했으며, MapReduce가 MongoBD 문서를 처리하기 위한 메커니즘이 되는 "Java development 2.0: MongoDB: (적절한) RDBMS 이동 기능을 제공하는 NoSQL 데이터 저장소"에서 이를 한 번 더 사용했다.

대용량 데이터 세트를 처리하기 위한 개념적인 프레임워크로서 MapReduce는 다수의 컴퓨터를 사용하여 분산된 문제 해결을 위해 고도로 최적화되었다. 프레임워크는 이름이 암시하는 대로 두 가지 함수로 구성된다. map 함수는 대용량 데이터 입력을 취하여 더 잘게 나누도록 설계되었으며, 그러면 이를 통해 어떠한 작업을 할 수 있는 다른 프로세스로 전달한다. reduce 함수는 map으로 수집되는 개별 응답을 요약하고 최종 출력으로 이를 렌더링한다.

Hadoop에서 Hadoop의 자체적인 기본 클래스를 확장하여 mapreduce 구현을 정의한다. 구현은 입력 및 출력 형식과 함께 이를 지정하는 구성으로 함께 묶인다. Hadoop은 구조화된 데이터가 들어있는 대용량 파일을 처리하기 위한 훌륭한 세트이다. Hadoop의 특히 편리한 측면 하나는 입력 파일의 원시 구문 분석을 처리하기 때문에, 사용자는 한 번에 한 행씩 다룰 수 있다. 따라서 map 함수를 정의하는 것은 실제로 텍스트의 수신되는 행에서부터 취하려는 것을 판별하는 문제에 불과하다.


데이터는 어디에나 있다!

미국 정부는 일반 시민이 크게 관심을 가지는 엄청난 양의 데이터를 제작한다. 다양한 정부 기관들은 미국 경제 상태 및 변화하는 사회적 인구 통계와 관련된 데이터를 자유롭게 분배한다. 미국 지질 조사소(USGS)는 국제적인 지진 데이터를 발표한다.

여러 소규모 지진들이 전 세계 곳곳에서 매일 발생한다. 이들 중 다수는 지각 내 깊은 곳에서 발생하여 아무도 인식하지 못하지만, 그럼에도 불구하고 청취 스테이션은 이를 기록한다. USGS는 주간 CSV(또는 콤마로 분리된 값) 파일 형태로 지진 데이터를 발표한다.

평균적인 주간 파일은 엄청나게 크지는 않다 — 겨우 100KB 정도이다. 하지만 이는 Hadoop을 학습하기 위한 기초로 쓰일 것이다. 그렇다고 하더라도 Hadoop이 훨씬 더 큰 규모의 데이터 세트를 처리할 수 있다는 점을 잊지 말자.

진동 추적

최근에 USGS 웹 사이트에서 다운로드한 CSV 파일은 다음 리스트 1과 같이 약 920개의 행으로 되어있다.


리스트 1. USGS 지진 데이터 파일의 행 개수
$> wc -l eqs7day-M1.txt 
  920 eqs7day-M1.txt

CVS 파일의 컨텐츠는 다음 리스트 2(즉, 처음 두 개의 행)에서 보는 것과 비슷하다.


리스트 2. CVS 파일의 처음 두 개의 행
$> head -n 2 eqs7day-M1.txt 
Src,Eqid,Version,Datetime,Lat,Lon,Magnitude,Depth,NST,Region
ci,14896484,2,"Sunday, December 12, 2010 23:23:20 UTC",33.3040,-116.4130,1.0,11.70,22,
  "Southern California"

특히 총 행의 합이 920개라는 점을 고려할 때에, 필자는 이를 Information Rich 파일이라고 할 것이다. 하지만 필자는 이 파일로 보고되는 그 주의 각 날짜에 발생하는 지진의 수만 알고자 한다. 그 다음에 이러한 7일에 대부분의 지진이 나타나는 일반적인 영역이 어디인지 알고자 한다.

필자의 첫 번째 생각은 일일 지진 수를 검색하기 위해 간단한 grep 명령을 사용할 수 있었다는 점이다. 파일을 살펴보면 데이터가 12월 12일에 시작하는 것으로 표시된다. 따라서 다음 리스트 3의 결과로 그 문자열의 grep -c를 수행한다.


리스트 3. 12월 12일에 지진이 얼마나 많이 발생하는가?
$> grep -c 'December 12' eqs7day-M1.txt 
98

Hadoop 설치

Hadoop을 이전에 설치하지 않았다면 이제 설치하자. 먼저, 최신 바이너리를 다운로드하고, 이를 압축 해제하여 경로에 Hadoop의 bin 디렉토리를 설정한다. 이렇게 하면 hadoop 명령을 직접 실행할 수 있다. Hadoop을 사용하면 앞으로 확인하는 대로 java 명령을 호출하는 것이 아니라 hadoop 명령을 실행해야 한다. Java 바이너리 파일(예를 들어, mapreduce 구현을 표현함)을 찾을 수 있는 것과 같이 옵션을 hadoop 명령으로 전달시킬 수 있다. 필자의 경우에 jar 파일을 작성하여, jar 내에서 실행하려는 작업이 어느 것인지 Hadoop에 알린다. 또한 필자의 애플리케이션을 Hadoop의 클래스 경로로 실행하는 데 필요한 추가 바이너리도 추가한다.

이제 12월 12일에 98개의 항목 또는 98개의 기록된 지진이 있었음을 알게 되었다. 행 아래로 이동하여 12월 11일, 10일 등에 grep을 수행할 수도 있을 것이다. 하지만, 그것은 필자가 생각하기에는 지루하다. 더욱 나쁜 것은 이를 이끌어 내기 위해 파일에 어느 날짜가 있는지 알아야 한다는 것이다. 필자는 그 내용에 실제로 신경을 쓰지 않으며, 어떠한 경우에는 그러한 정보에 액세스 권한이 없을 수 있다. 실제로 필자는 어느 7일 범위에서나 주어진 각 날짜의 숫자만 알고자 하고, Hadoop을 통해 그러한 정보를 간편하게 얻을 수 있다.

Hadoop은 필자의 첫 번째와 두 번째 질문에 응답하는 정보의 몇 가지 부분만 필요로 한다. 다시 말해서, 처리할 입력이 어느 것이며, mapreduce를 어떻게 다루느냐이다. 또한 모두 함께 묶는 작업도 제공해야 할 것이다. 하지만, 그 코드에 작업을 시작하기 전에 전부 필자의 CSV 데이터 순서로 되어 있는지 확인하는 데 수분이 걸릴 것이다.


opencsv로 데이터 구문 분석

지진 CSV 파일의 첫 행인 헤더를 제외하고 각 행은 콤마로 분리된 데이터 값의 시리즈이다. 필자는 세 가지 데이터 부분인 각 지진의 날짜, 위치 및 규모에 주로 관심이 있다. 이러한 데이터를 확보하기 위해 opencsv라는 실용적인 오픈 소스 라이브러리를 사용할 것이며, 이는 CSV 파일을 구문 분석하는 데 유용하다.

테스트를 우선 시행하는 사람이 되기 위해, 목록 4와 같이 CSV 파일에서 확보한 샘플 행에서 원하는 정보를 확보할 수 있는지 확인하는 빠른 JUnit 테스트를 작성하여 시작할 것이다.


리스트 4. CSV 행 구문 분석하기
public class CSVProcessingTest {

 private final String LINE = "ci,14897012,2,\"Monday, December 13, 2010 " +
            "14:10:32 UTC\",33.0290,-115." +
            "5388,1.9,15.70,41,\"Southern California\"";

 @Test
 public void testReadingOneLine() throws Exception {
  String[] lines = new CSVParser().parseLine(LINE);

  assertEquals("should be Monday, December 13, 2010 14:10:32 UTC",
    "Monday, December 13, 2010 14:10:32 UTC", lines[3]);

  assertEquals("should be Southern California",
    "Southern California", lines[9]);

  assertEquals("should be 1.9", "1.9", lines[6]);
 }
}

리스트 4에서 확인 가능한 대로, opencsv는 콤마로 분리된 값으로 매우 간단하게 작업하게 해준다. 구문 분석기는 간단하게 String의 배열을 리턴하므로, 위치 상의 값을 확보할 수 있다(Java 언어에서 배열 및 콜렉션 액세스가 제로 기반인 것을 기억하자).

데이터 형식 변환하기

MapReduce로 작업할 때에 map 함수의 작업은 일부 키에 추가로 작업하기 위해 일부 값을 선택하는 것이다. 다시 말해서, map은 주로 두 가지 요소로 작업하고 리턴한다. 이는 키와 값이다. 이전의 요구사항으로 돌아가서 필자는 우선 매일 지진이 얼마나 발생하는지 알아내고자 한다. 따라서, 지진 파일을 분석할 때에 두 가지 값을 도출할 것이다. 즉, 키는 날짜가 될 것이고, 값은 카운터가 될 것이다. 그러면 reduce 함수가 카운터를 합하여(이는 1의 값으로 정수임), 대상 지진 파일에서 날짜당 발생하는 횟수를 제공할 것이다.

24시간 기간에 관심이 있기 때문에, 각 파일에서 날짜의 시간 부분을 삭제해야 할 것이다. 다음 리스트 5에서 수신되는 파일에서 특정 날짜 형태를 더 일반적인 24시간 기간 날짜로 어떻게 전환하는지 유효성 검증하는 빠른 테스트를 작성한다.


리스트 5. 날짜 형식 변환
@Test
public void testParsingDate() throws Exception {
 String datest = "Monday, December 13, 2010 14:10:32 UTC";
 SimpleDateFormat formatter = new SimpleDateFormat("EEEEE, MMMMM dd, yyyy HH:mm:ss Z");
 Date dt = formatter.parse(datest);

 formatter.applyPattern("dd-MM-yyyy");
 String dtstr = formatter.format(dt);
 assertEquals("should be 13-12-2010", "13-12-2010", dtstr);
}

리스트 5에서 SimpleDateFormat Java 오브젝트를 사용하여 Monday, December 13, 2010 14:10:32 UTC의 CSV 파일 형식에서 날짜 String을 더 일반적인 13-12-2010으로 형식화했다.


Hadoop의 map 및 reduce

이제 CSV 파일과 데이터 형식을 어떻게 처리할 것인지에 대한 작업을 했으니, Hadoop에서 mapreduce 함수를 구현하여 시작할 차례가 되었다. 이 프로세스는 Hadoop이 명시 유형 안전을 선호하기 때문에, Java 일반을 이해하는 것이 필요하다.

Hadoop으로 map 구현을 정의할 때에 Hadoop의 Mapper 클래스를 간단하게 확장한다. 그러면 발신되는 키와 값 모두에 대한 명시 유형을 지정하기 위해 일반을 사용할 수 있다. 또한 유형 절은 수신되는 키와 값을 설명하며, 이는 파일 읽기의 경우에 이는 각각 바이트 개수와 텍스트의 행이다.

EarthQuakesPerDateMapper 클래스는 Hadoop의 Mapper 오브젝트를 확장한다. 이는 명시적으로 출력 키를 Text 오브젝트로 설명하고 그 값을 IntWritable로 설명하며, 이는 본질적으로 정수인 Hadoop 특화된 클래스이다. 또한 클래스 절에서 처음 두 개의 유형은 LongWritableText이며, 이는 각각 바이트 개수와 텍스트의 행임을 참고하자.

클래스 정의에서 유형 절로 인해 map 메소드로 수신되는 매개변수 유형은 context.write 절 내에서 이 메소드의 출력과 함께 설정된다. 다른 것을 지정하려고 시도하면, 컴파일러 문제가 발생하거나 Hadoop에서 유형 불일치를 설명하는 메시지가 표시되며 오류가 나타날 것이다.


리스트 6. 맵핑 구현
public class EarthQuakesPerDateMapper extends Mapper<LongWritable, 
  Text, Text, IntWritable> {
 @Override
 protected void map(LongWritable key, Text value, Context context) throws IOException,
   InterruptedException {

  if (key.get() > 0) {
   try {
     CSVParser parser = new CSVParser();
     String[] lines = parser.parseLine(value.toString());

     SimpleDateFormat formatter = 
       new SimpleDateFormat("EEEEE, MMMMM dd, yyyy HH:mm:ss Z");
     Date dt = formatter.parse(lines[3]);
     formatter.applyPattern("dd-MM-yyyy");

     String dtstr = formatter.format(dt);
     context.write(new Text(dtstr), new IntWritable(1));
   } catch (ParseException e) {}
  }
 }
}

리스트 6에서 map 구현은 간단하다. 즉, Hadoop은 기본적으로 입력 파일에서 발견하는 텍스트의 각 행에 대해 이 클래스를 호출한다. CSV의 헤더를 처리하려는 시도를 방지하기 위해 먼저, 바이트 개수(key 오브젝트)가 0이 아닌지 확인한다. 그 다음에, 목록 4와 5에서 이미 확인한 것을 수행한다. 즉, 수신되는 날짜를 취하여 변환한 다음에 이를 발신 키로 설정한다. 또한 하나의 개수인 1을 제공한다. 다시 말해서, 각 날짜에 대해 카운터를 코드했으며 reduce 구현이 호출될 때에, 키와 값의 콜렉션을 받을 것이다. 이 경우에 키는 리스트 7과 같이 날짜와 그 값이 될 것이다.


리스트 7. map 출력과 reduce 입력의 논리적 보기
"13-12-2010":[1,1,1,1,1,1,1,1]
"14-12-2010":[1,1,1,1,1,1]
"15-12-2010":[1,1,1,1,1,1,1,1,1]

context.write(new Text(dtstr), new IntWritable(1))(리스트 6에 있음) 행이 리스트 7과 같이 논리적 콜렉션을 빌드하였음을 참고하자. 아마 이미 확인한 대로, context는 다양한 정보 부분을 보유하는 Hadoop 데이터 구조이다. 이 contextreduce 구현으로 전달되며, 이는 이러한 1 값들을 취하고 이를 합할 것이다. 결과적으로, reduce 구현은 논리적으로 다음 리스트 8에서와 같은 데이터 구조를 작성한다.


리스트 8. reduce 출력의 보기
"13-12-2010":8
"14-12-2010":6
"15-12-2010":9

reduce 구현이 리스트 9에 표시된다. Hadoop의 MapperReducer는 매개변수화되었다. 즉, 처음 두 개의 매개변수는 수신되는 키 유형(Text)과 값 유형(IntWritable)이고, 나중 두 개의 매개변수는 출력 유형인 키와 값이며, 이 경우에 이는 동일하다.


리스트 9. reduce 구현
public class EarthQuakesPerDateReducer extends Reducer<Text, IntWritable, Text, 
  IntWritable> {
 @Override
 protected void reduce(Text key, Iterable<IntWritable> values, Context context)
  throws IOException, InterruptedException {
  int count = 0;
  for (IntWritable value : values) {
   count++;
  }
  context.write(key, new IntWritable(count));
 }
}

reduce 구현은 지극히 간단하다. 리스트 7에서 지적한 대로, 수신되는 값은 실제로 값의 콜렉션이며, 이 경우에는 1 값들의 콜렉션을 의미한다. 필자가 수행하는 것은 이를 합한 다음, 날짜와 개수를 표현하는 새로운 키-값 쌍을 작성하는 것이다. 그 다음에 reduce 코드가 기본적으로 리스트 8에서 확인한 행을 산출한다. 논리적 플로우는 다음과 같다.

"13-12-2010":[1,1,1,1,1,1,1,1] -> "13-12-2010":8

이 목록의 요약 양식은 물론, map -> reduce이다.


Hadoop Job 정의하기

이제 mapreduce 구현을 코드 작성했으니, 남은 일은 Hadoop Job으로 모두 연결하는 것이다. Job을 정의하는 것은 간단하다. 즉, 입력과 출력인 mapreduce 구현(리스트 6리스트 9에 표시됨) 및 출력 유형을 제공한다. 이 경우에 출력 유형은 reduce 구현에 사용되는 것과 동일하다.


리스트 10. Job은 map과 reduce를 함께 묶는다
public class EarthQuakesPerDayJob {

 public static void main(String[] args) throws Throwable {

  Job job = new Job();
  job.setJarByClass(EarthQuakesPerDayJob.class);
  FileInputFormat.addInputPath(job, new Path(args[0]));
  FileOutputFormat.setOutputPath(job, new Path(args[1]));

  job.setMapperClass(EarthQuakesPerDateMapper.class);
  job.setReducerClass(EarthQuakesPerDateReducer.class);
  job.setOutputKeyClass(Text.class);
  job.setOutputValueClass(IntWritable.class);

  System.exit(job.waitForCompletion(true) ? 0 : 1);
 }
}

리스트 10에서 두 개의 매개변수를 취하는 main 메소드로 모두 함께 묶었다. 즉, 이는 지진 CSV 파일인 디렉토리와 결과 보고서가 작성되어야 하는 디렉토리(Hadoop은 이 디렉토리를 작성하는 것을 선호함)이다.

이러한 작은 프레임워크를 실행하려면 이러한 클래스를 jar로 해야 할 것이다. 또한 Hadoop에 opencsv 바이너리를 찾을 수 있는 위치를 알려야 할 것이다. 그 다음에 리스트 11과 같이 명령행을 통해 Hadoop을 실행할 수 있다.


리스트 11. Hadoop 실행하기
$> export HADOOP_CLASSPATH=lib/opencsv-2.2.jar
$> hadoop jar target/quake.jar com.b50.hadoop.quake.EarthQuakesPerDayJob
   ~/temp/mreduce/in/ ~/temp/mreduce/out

이 코드를 실행하면, Hadoop이 그 작업 수행을 시작하면 화면에 많은 텍스트가 날라다니는 것을 확인할 것이다. 사용 중인 CSV 파일은 Hadoop이 처리하기 위해 빌드한 큰 개와 비교하면 강아지에 불과하다는 점을 유의하자. 처리 성능에 따라 Hadoop은 수 초 내에 이를 완료해야 한다.

완료하면 가상으로 편집기를 통해 출력 파일의 컨텐츠를 볼 수 있다. 또 다른 옵션은 리스트 12에서 수행한 것처럼 hadoop 명령을 직접 사용하는 것이다.


리스트 12. Hadoop의 출력 읽기
$> hadoop dfs -cat part-r-00000 
05-12-2010      43
06-12-2010      143
07-12-2010      112
08-12-2010      136
09-12-2010      178
10-12-2010      114
11-12-2010      114
12-12-2010      79

독자가 필자와 비슷하다면 리스트 12에서 확인할 첫 번째 내용은 일일 지진의 수 그 자체이다 — 12월 9일에 178만 있다! Hadoop이 필자가 수행하려는 바로 그것을 수행했다는 점도 인식하길 바란다. 필자의 범위 내 모든 날짜에 대해 지진 발생 수를 깔끔하게 표로 만들었다.


또 다른 Mapper 쓰기

다음으로, 지진이 발생하는 위치를 알아내고, 어느 위치가 날짜 범위에서 대부분의 지진을 로그하는지 어떻게든 빠르게 측정하고자 한다. 자, 아마 추측한 대로, Hadoop을 통해 그렇게 간편하게 수행된다. 이 경우에 키는 날짜가 아니라 위치이다. 따라서, 새 Mapper 클래스를 쓴다.


리스트 13. 새 map 구현
public class EarthQuakeLocationMapper extends Mapper<LongWritable, Text, Text,
  IntWritable> {
 @Override
 protected void map(LongWritable key, Text value, Context context) throws IOException,
  InterruptedException {
  if (key.get() > 0) {
   String[] lines = new CSVParser().parseLine(value.toString());
   context.write(new Text(lines[9]), new IntWritable(1));
  }
 }
}

날짜를 확보하고 이를 변환하는 것이 아니라, 리스트 13에서 수행한 전부는 위치를 취하는 것이었으며, 이는 CSV 배열에서 최종 위치상의 항목이었다.

위치와 그 숫자의 거대한 목록이 아니라, 결과를 어느 7일 기간에나 10회 이상의 지진이 나타난 위치로 제한하려고 한다.


리스트 14. 어디에서 지진이 더 많이 나타나는가?
public class EarthQuakeLocationReducer extends Reducer<Text, IntWritable, Text,
  IntWritable> {
 @Override
 protected void reduce(Text key, Iterable<IntWritable> values, Context context)
  throws IOException, InterruptedException {
  int count = 0;
  for (IntWritable value : values) {
   count++;
  }
  if (count >= 10) {
   context.write(key, new IntWritable(count));
  }
 }
}

리스트 14에서 코드는 리스트 9에 있는 것과 매우 유사하다. 하지만 이 경우에 10개 이상의 합으로 출력을 제한했다. 그 다음으로, 새로운 답변을 얻기 위해 mapreduce를 또 다른 Job 구현과 묶고, jar로 만들어 Hadoop을 정상으로 실행할 수 있다.

hadoop dfs 명령을 실행하면 다음과 같이 요청한 새 값을 표시한다.


리스트 15. 위치별 지진
$> hadoop dfs -cat part-r-00000 
Andreanof Islands, Aleutian Islands, Alaska     24
Arkansas        40
Baja California, Mexico 101
Central Alaska  74
Central California      68
Greater Los Angeles area, California    16
Island of Hawaii, Hawaii        16
Kenai Peninsula, Alaska 11
Nevada  15
Northern California     114
San Francisco Bay area, California      21
Southern Alaska 97
Southern California     115
Utah    19
western Montana 11


리스트 15에서 배울 내용은 무엇인가? 자, 먼저 멕시코에서 알래스카에 이르는 북미의 서부 해안은 불안정한 지역이다. 두 번째로, 아칸소는 명백히 단층선 근처에 위치하고 있으며, 이는 필자가 인식하지 못한 점이다. 마지막으로 독자가 북부 또는 남부 캘리포니아(많은 소프트웨어 개발자들이 거주함)에 산다면 독자 주변의 지반은 대략 매 13분마다 흔들린다.


결론

Hadoop으로 데이터를 분석하는 것은 간편하고 효율적이므로, 데이터 분석을 위해 제공해야 하는 내용조차도 수박 겉핥기식으로 다루지 않았다. Hadoop은 실제로 mapreduce를 실행하는 다양한 노드의 조정을 처리하는 분산된 방식으로 실행하도록 설계되었다. 예제의 목적을 위해 이 기사에서 보잘 것 없는 파일 하나로 하나의 JVM에서 Hadoop을 실행했다.

Hadoop은 보유하기에 그 자체로 탁월한 도구이며, 또한 이와 관련하여 하위 프로젝트에서부터 클라우드 기반 Hadoop 서비스에 이르기까지 전체적이며 성장하는 에코시스템도 있다. Hadoop 에코시스템은 프로젝트 배후에 풍부한 커뮤니티를 시연한다. 커뮤니티에서 파생된 많은 도구들은 전역 비즈니스 활동으로 대용량 데이터 분석의 실행 가능성을 보여준다. Hadoop을 통해 분산 데이터 마이닝과 분석은 Google과 Yahoo!를 포함하지만 이에 국한되지 않은 모든 종류의 소프트웨어 혁신자와 기업가들이 사용할 수 있다.


참고자료

교육

  • Java development 2.0: 이 dW 시리즈에서는 Java 개발 환경을 다시 정의하는 각종 기술을 탐구한다. 최신 주제로는 MongoDB(2010년 9월), CouchDB(2009년 11월) 및 Objectify AppEngine(2010년 11월)이 있다.

  • "Hadoop을 이용한 분산 데이터 처리, Part 1: 시작"(M. Tim Jones저, developerWorks, 2010년 5월): 이 기사는 — 시리즈의 첫 번째 — HDFS(Hadoop file system)와 일반적으로 사용되는 노드 유형을 비롯한 Hadoop 프레임워크에 대해 살펴본다. 단일 노드 Hadoop 클러스터를 설치 및 구성하는 방법을 학습한 후 MapReduce 애플리케이션에 대해 자세히 설명한다. 마지막으로 Hadoop의 핵심 웹 인터페이스를 사용하여 Hadoop을 모니터링 및 관리하는 방법에 대해 살펴본다. 또한 Part 2Part 3도 참조한다.

  • "클라우드에서 MapReduce 및 로드 밸런싱 사용하기(Kirpal A. Venkatesh 외 저, developerWorks, 2010년 7월): Hadoop MapReduce 및 가상화로 노드 성능을 개선하는 방법을 배워보자.

  • "A profile of Apache Hadoop MapReduce computing efficiency, Part 1"(Paul Burkhardt저, Cloudera Development Center, 2010년 12월): MapReduce 애플리케이션이 얼마나 효율적으로 컴퓨팅 자원을 사용하는지에 대한 두 개의 파트로 된 설명이다. 처음 반은 Hadoop MapReduce 애플리케이션을 평가하는 것과 관련되는 컴퓨팅 효율성의 개요이다.

  • "Hadoop companies everywhere"(Alex Handy저, SD Times, 2009년 7월): 회사는 매일 데이터를 더 많이 생성하지만, 이들 중 많은 수가 비즈니스 인텔리전스를 이끌어내지 않는다. 이는 기회라고 쓰고 Handy라고 읽는다.

  • 이런 기술 주제와 다른 기술 주제에 대한 서적 정보는 Java technology bookstore를 참조한다.

  • developerWorks Java 기술 영역: Java 프로그래밍과 관련된 모든 주제를 다루는 여러 편의 기사를 찾아보자.

제품 및 기술 얻기

토론

  • developerWorks 커뮤니티에 참여하자. 개발자가 이끌고 있는 블로그, 포럼, 그룹 및 Wiki를 살펴보면서 다른 developerWorks 사용자와 의견을 나눌 수 있다.

필자소개

Andrew Glover 사진

Andrew Glover는 Stelligent Incorporated의 사장이다. 회사들이 코드 품질을 일찍 그리고 자주 모니터할 수 있게 하는 효과적인 개발자 테스팅 전략과 지속적 통합 기법으로 소프트웨어 품질 문제를 해결하는 것을 돕고 있다. Andy의 저서 목록은 그의 블로그를 보라.

 

Posted by 1010
01.JAVA/Java2012. 12. 6. 10:25
반응형

-- class 주석

/**
* <PRE>
* 보세운송 조회를 한다.
* </PRE>
* @author anhanho
* @version 1.0
* @since 13/10/2011
* @modified
* @modifier
*/

 

-- method 주석

/**
  * @description 행정몰수  게시 (5일 ) + 48시간을  지나면  행정몰수확정이 된다.
  * @param List arg0
  * @return void
  * @throws Exception
  */


 

Posted by 1010
01.JAVA/Java2012. 11. 14. 17:37
반응형

 

 

 

junit 에 VM 설정과 eclips.ini 에 설정은 다름...

Posted by 1010
01.JAVA/Java2012. 10. 17. 07:13
반응형

출처 : http://stackoverflow.com/questions/109383/how-to-sort-a-mapkey-value-on-the-values-in-java

 

It seems much easier than all of the foregoing. Use a TreeMap as follows:

public class Testing { 
 
    public static void main(String[] args) { 
 
        HashMap<String,Double> map = new HashMap<String,Double>(); 
        ValueComparator bvc =  new ValueComparator(map); 
        TreeMap<String,Double> sorted_map = new TreeMap<String,Double>(bvc); 
 
        map.put("A",99.5); 
        map.put("B",67.4); 
        map.put("C",67.4); 
        map.put("D",67.3); 
 
        System.out.println("unsorted map: "+map); 
 
        sorted_map.putAll(map); 
 
        System.out.println("results: "+sorted_map); 
    } 
} 
 
class ValueComparator implements Comparator<String> { 
 
    Map<String, Double> base; 
    public ValueComparator(Map<String, Double> base) { 
        this.base = base; 
    } 
 
    // Note: this comparator imposes orderings that are inconsistent with equals.     
    public int compare(String a, String b) { 
        if (base.get(a) >= base.get(b)) { 
            return -1; 
        } else { 
            return 1; 
        } // returning 0 would merge keys 
    } 
} 

Output:

    unsorted map: {D=67.3, A=99.5, B=67.4, C=67.4}
    results: {D=67.3, B=67.4, C=67.4, A=99.5}
    

 

Posted by 1010
01.JAVA/Java2012. 9. 21. 06:58
반응형

import java.awt.Graphics2D;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;

import javax.imageio.ImageIO;

public class CommonUtil {
 public CommonUtil(){
 }
 
 /**
  * 썸네일 만들기
  * @param width
  * @param height
  * @param loadFile
  * @param saveFile
  * @param zoom
  * @throws IOException
  */
 public static void createThumbnail(int width, int height, String loadFile, String saveFile, int zoom) throws IOException{
  File save = new File(saveFile.replaceAll("/", "\\"+File.separator));
  FileInputStream fis = new FileInputStream(loadFile.replaceAll("/", "\\"+File.separator));
  BufferedImage im = ImageIO.read(fis);
  
  if (zoom<=0) zoom = 1;
  
  BufferedImage thumb = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
  Graphics2D g2 = thumb.createGraphics();
  
  g2.drawImage(im, 0, 0, width, height, null);
  ImageIO.write(thumb, "jpg", save);
 }
 
 
 /**
  * 썸네일 삭제
  * @param thumbnail_path
  */
 public static void deleteThumbnail(String thumbnail_path){
  File file = new File(thumbnail_path);
  if(file.exists()){
   file.delete();
  }
 }
 
 public static void main(String args[]){
  String loadFile = "http://localhost:8080/2010/07/15/LZHABUhgGy1279183572640.JPG";
  String saveFile = "D://dev/test.jpg";
  int zoom = 5;
  
  try {
   CommonUtil.createThumbnail(124, 144, loadFile, saveFile, zoom);
  } catch (IOException e) {
   e.printStackTrace();
  }
 }
}

 

출처 : http://pskppoppo.egloos.com/2994706

Posted by 1010
01.JAVA/Java2012. 9. 21. 06:55
반응형

사용자의 로그정보가 필요해 아래와같이 간단하게 만들어봤다.

public void makeFreePaidLog(HttpServletRequest request, String academyCode, String date) throws IOException, Exception{
StringBuffer sb = new StringBuffer();
SimpleDateFormat formatter = new SimpleDateFormat ("yyyy-MM-dd HH:mm:ss.SSS");

Calendar cal = Calendar.getInstance();
String today = formatter.format(cal.getTime());

String str = "[" +today + "]";
sb.append("Browser Info = " + request.getHeader("User-AGENT") + " MIS_ID = " + SessionUtil.getMis_Id(request));
sb.append(", USER_NM = " + SessionUtil.getUserNm(request) + ", ACAD_CD = " + academyCode);
String log = str + sb.toString();

try{
BufferedWriter bw = new BufferedWriter(new FileWriter("D:\\FreePaidLog_"+date+".txt", true));
bw.write(log);
bw.newLine();
bw.flush();
bw.close();
}catch(IOException ie){
System.err.println("Error");
System.exit(1);
}
}

위에서 중요한 부분은 new FileWriter()하는부분. 뭐가 문제인지는 모르겠으나 FileWriter 객체 생성할때 다른 class나 새로운 객체를 생성하여 매개인자로 넣으면 에러가난다.

Posted by 1010
01.JAVA/Java2012. 9. 21. 06:52
반응형

간단하긴 한걸까...

java.util.Calendar cal = java.util.Calendar.getInstance();
//일단 Calendar 객체

int year = 2011;
//수능일 기준으로 잡아봤다.
int month = 11;
int date = 10;

long now_day = cal.getTimeInMillis();
//현재 시간

cal.set(year, month-1, date);
//목표일을 cal에 set

long event_day = cal.getTimeInMillis();
//목표일에 대한 시간
long d_day = (event_day - now_day) / (60*60*24*1000);

일단 목표일이 현재 일보다는 커야한다는 조건이 붙어야 한다. 이걸 parameter 세개 받아서 며칠남았는지 딱딱 return 시켜주는 공통 method로 만들어 봐야하는데 지금 귀찮음.

Posted by 1010
01.JAVA/Java2012. 9. 21. 06:46
반응형
1. split
String str = "sukeun.park, ,010-0000-0000";
String [] values = "str.split(",")";

for( int x = 0; x < values.length; x++ ){
System.out.println( "문자(열) " + (x+1) + " : " + values[x] );
}

print>
문자(열) 1 : sukeun.park
문자(열) 2 :
문자(열) 3 : 010-0000-0000

2. StringTokenizer
String str = "sukeun.park, ,010-0000-0000";
StringTokenizer tokens = new StringTokenizer(str, ",");

for(int x = 1; tokens.hasMoreElements(); x++;){
System.out.println( "문자(열) " + x + " : " + tokens.nextToken());
}

print>
문자(열) 1 : sukeun.park
문자(열) 2 : 010-0000-0000

차이점 알겠나?

 

Posted by 1010
01.JAVA/Java2012. 9. 21. 06:45
반응형

사용자의 로그정보가 필요해 아래와같이 간단하게 만들어봤다.

public void makeFreePaidLog(HttpServletRequest request, String academyCode, String date) throws IOException, Exception{
StringBuffer sb = new StringBuffer();
SimpleDateFormat formatter = new SimpleDateFormat ("yyyy-MM-dd HH:mm:ss.SSS");

Calendar cal = Calendar.getInstance();
String today = formatter.format(cal.getTime());

String str = "[" +today + "]";
sb.append("Browser Info = " + request.getHeader("User-AGENT") + " MIS_ID = " + SessionUtil.getMis_Id(request));
sb.append(", USER_NM = " + SessionUtil.getUserNm(request) + ", ACAD_CD = " + academyCode);
String log = str + sb.toString();

try{
BufferedWriter bw = new BufferedWriter(new FileWriter("D:\\FreePaidLog_"+date+".txt", true));
bw.write(log);
bw.newLine();
bw.flush();
bw.close();
}catch(IOException ie){
System.err.println("Error");
System.exit(1);
}
}

위에서 중요한 부분은 new FileWriter()하는부분. 뭐가 문제인지는 모르겠으나 FileWriter 객체 생성할때 다른 class나 새로운 객체를 생성하여 매개인자로 넣으면 에러가난다.

Posted by 1010
01.JAVA/Java2012. 9. 20. 07:52
반응형

출처 : http://joke00.tistory.com/69

 

자바 책을 훑어보던 중.
Exception 관련해서 assertion 부분이 나왔다. ( 왜.. 예전엔 미처 몰랐을까;;;;)

assertion 은 디버깅시에 익셉션 에러 검증 구문으로 인용하기 쉽다.
throws 나 try ~ catch 블럭과 동일한 역할을 합니다.

사용법
---------------------------------------------------------------------------------------------
assert [ true or false 를 나타내는검증구문] : [검증에서 false 발생시 출력할 내용]
---------------------------------------------------------------------------------------------

1. 이클립스를 열어 테스트를 하나 만듭니다.



2. 해당 프로젝트를 선택 후 마우스 오른쪽을 누르면 properties 를 눌러 Run/Debug Settings 를 선택합니다.
해당 프로젝트가 보이네요~ edit 를 누릅니다



3. Arguments 탭으로 이동후 VM arguments 를 입력합니다.
-ea 는 assertion 실행
-ea:클래스명 : 해당 클래스만 assetion 을 실행
-ea:... : 현재 패키지 내에 있는 클래스들만 assertion 실행
-ea<package명> : 해당 package 명 내의 클래스들만 assetion 실행

-da 로 했을 경우에는 반대로 해당되는 경우에만 assertion 을 실행시키지 않음



4. ctrl + f11 을 눌러 실행을 시킵니다.
아름과 같은 익셉션 구문과 함께 어디서 익셉션이 났는지를 알려준답니다 ^^

Posted by 1010
01.JAVA/Java2012. 9. 20. 07:03
반응형

Quick Start

Have you downloaded? Good. Now let’s get your first compiled spreadsheet up and running. I’ll show you how to…

  • create a spreadsheet defining a customized line item price computation,
  • make AFC compile it to Java classes,
  • use those classes in your application,
  • look at the generated code, and
  • save and reload the classes from a .jar file.

The Problem

This is a fictional example about finding the total price for a line item with

  • a base article price,
  • a count of items, and
  • the customer category (for automatic rebates).

We will flesh out the following bit of code:

// Compile price finding factory and strategy implementation from spreadsheet:
EngineBuilder builder = SpreadsheetCompiler.newEngineBuilder();
builder.loadSpreadsheet( new File( PATH, "CustomPriceFormula.xls" ) );
builder.setFactoryClass( PriceFinderFactory.class );
builder.bindAllByName();
Engine engine = builder.compile();
PriceFinderFactory factory = (PriceFinderFactory) engine.getComputationFactory();

// Use it to compute a line item price:
LineItem item = getCurrentLineItem();
PriceFinder priceFinder = factory.newInstance( item );
BigDecimal price = priceFinder.getPrice();

The Spreadsheet

Here’s the spreadsheet-defined formula our user wants the application to use:

A B C D E
1 Input Values
2 Article Price 500.00
3 Item Count 5
4 Customer Category C
5
6 Intermediate Values
7 Categories A B C D
8 Rebates 6% 4% 1% 0%
9 Effective Rebate =HLOOKUP(B4,B7:E8,2.0)
10 Base Price =B2*B3
11
12 Output Values
13 Price =B10*(1.0-B9)

Please create this spreadsheet now and save it somewhere under the name CustomPriceFormula.xls.

The Project

Create a new Java project for this demo application now. Add to it references to the following libraries you obtained from the download:

build/formulacompiler-runtime.jar
build/formulacompiler-compiler.jar
build/formulacompiler-spreadsheet.jar
build/formulacompiler-spreadsheet-excel-xls.jar
build/formulacompiler-decompiler.jar
build/lib/asm-x.y.jar
build/lib/asm-commons-x.y.jar
build/lib/jxl.jar
build/lib/jode-decompiler.jar

Then create a main class in it. Unless your IDE can find and organize imports automatically, you might also want to add these to your main class:

import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.PrintStream;
import java.math.BigDecimal;

import org.formulacompiler.compiler.CompilerException;
import org.formulacompiler.compiler.SaveableEngine;
import org.formulacompiler.decompiler.ByteCodeEngineSource;
import org.formulacompiler.decompiler.FormulaDecompiler;
import org.formulacompiler.runtime.Engine;
import org.formulacompiler.runtime.EngineException;
import org.formulacompiler.runtime.FormulaRuntime;
import org.formulacompiler.spreadsheet.EngineBuilder;
import org.formulacompiler.spreadsheet.SpreadsheetCompiler;

The Interfaces

In essence, what AFC compiles from the spreadsheet is a computation strategy implementation. So we need the customary factory and doer interfaces that go with a strategy:

public static interface PriceFinder {
  BigDecimal getPrice();
}

public static interface PriceFinderFactory {
  PriceFinder newInstance( LineItem item );
}

Paste these directly into your main class (they are nested classes in this example, but AFC is just as happy with top-level classes).

We’ll also need the line item, which provides input data to the computation:

public static class LineItem {
  public BigDecimal getArticlePrice() { return BigDecimal.valueOf( 112.00 ); } 
  public int getItemCount() { return 10; }
  public String getCustomerCategory() { return "B"; }
}

It’s obviously a dummy. Paste it into the main class as well, together with the following dummy getter:

private LineItem getCurrentLineItem() {
  return new LineItem();
}

The Compilation

We’re ready to compile now. Add the following two methods, that I’ll explain shortly:

private SaveableEngine compile() throws FileNotFoundException, IOException, CompilerException, EngineException
{
  EngineBuilder builder = SpreadsheetCompiler.newEngineBuilder();
  builder.loadSpreadsheet( new File( PATH, "CustomPriceFormula.xls" ) );
  builder.setFactoryClass( PriceFinderFactory.class );
  builder.bindAllByName();
  return builder.compile();
}

private PriceFinderFactory factoryFor( Engine engine )
{
  return (PriceFinderFactory) engine.getComputationFactory();
}

Also add a constant for the path where you saved the spreadsheet. In my code, it’s:

public static final File PATH = new File( "src/test/data/org/formulacompiler/tutorials" );

Let’s go through this now:

  • First, we get a new engine builder. It gives us a simplified API onto AFC.
  • We tell the builder to load the spreadsheet you created earlier on. AFC detects the appropriate loader to use by the file’s extension. It currently supports Microsoft Excel (.xls) and OpenOffice Calc (.ods), and maybe others – check the release notes for details.
  • We inform the builder of our factory class. It can usually infer from this the input interface, LineItem, and the output interface, PriceFinder, by itself (if not, you can given them explicitly; you can then also omit the factory interface altogether).
  • We tell the builder to bind spreadsheet cells to our interface methods by name. In short, any cell named after a method on the input interface, LineItem, gets its value from that method in the compiled formula. A cell named after a method on the output interface, PriceFinder, is used to implement that method. (The binder is smart about dropping the get prefix on method names and is not case sensitive.)
  • But wait, we don’t have any cell names in this spreadsheet. What’s going on? AFC automatically creates cell names from row titles (string values in column A are applied as names for corresponding cells in column B) when a sheet has no cell names and you use bindAllByName().
  • That’s it. We tell the builder to compile the thing. It returns a so-called engine (which we’ll later use to save, reload, and decompile), but the most important method on the engine is the one we use on our second method: getComputationFactory().

The Computation

We now look at how the resulting price finder is used. Since AFC compiles spreadsheets to regular JVM classes, there’s no magic at all when using a precompiled computation:

private BigDecimal compute( PriceFinderFactory factory )
{
  PriceFinder priceFinder = factory.newInstance( getCurrentLineItem() );
  return priceFinder.getPrice();
}

So let’s make this runnable:

public static void main( String[] args ) throws Exception
{
  QuickStart app = new QuickStart();
  SaveableEngine engine = app.compile();
  PriceFinderFactory factory = app.factoryFor( engine );
  BigDecimal price = app.compute( factory );
  System.out.println( "The result is " + price );
}

Go ahead. Run it.

The Code

Would you also like to know just what exactly the generated PriceFinder implementation does? You can. AFC wraps a nifty external library, Jode, to decompile generated code to plain Java source again for just this purpose. Let’s use this:

private void decompile( SaveableEngine engine ) throws Exception
{
  ByteCodeEngineSource source = FormulaDecompiler.decompile( engine );
  source.saveTo( new File( "temp/test/decompiled/quickstart" ) );
}

You can change the output folder, of course. Now add the following at the end of the main method:

app.decompile( engine );

Run it again. Then look into the output folder. You should find the following there. First, the generated computation:

package org.formulacompiler.gen;
import java.math.BigDecimal;

import org.formulacompiler.runtime.Computation;
import org.formulacompiler.runtime.FormulaException;
import org.formulacompiler.runtime.internal.Environment;
import org.formulacompiler.runtime.internal.RuntimeDouble_v2;
import org.formulacompiler.runtime.internal.Runtime_v2;
import org.formulacompiler.tutorials.QuickStart;

final class $Root implements Computation, QuickStart.PriceFinder
{
    private final QuickStart.LineItem $inputs;
    final Environment $environment;
    private String[] $constarr$0;
    
    $Root(QuickStart.LineItem lineitem, Environment environment) {
        $environment = environment;
        $inputs = lineitem;
    }
    
    final double get$0() {
        return (get$1() * get$2()
                * (1.0 - $idx$0(Runtime_v2.fun_MATCH_Ascending(get$3(),
                                                               $constarr$0(),
                                                               $environment)
                                - 1)));
    }
    
    public final BigDecimal getPrice() {
        return BigDecimal.valueOf(Runtime_v2.checkDouble(get$0()));
    }
    
    final String[] $constarr$0() {
        if ($constarr$0 == null)
            $constarr$0 = new String[] { "A", "B", "C", "D" };
        return $constarr$0;
    }
    
    final double $idx$0(int i) {
        switch (i) {
        case 0:
            return 0.06;
        case 1:
            return 0.04;
        case 2:
            return 0.01;
        case 3:
            return 0.0;
        default:
            throw new FormulaException
                      ("#VALUE/REF! because index is out of range in INDEX");
        }
    }
    
    final double get$1() {
        return RuntimeDouble_v2.numberToNum($inputs.getArticlePrice());
    }
    
    final double get$2() {
        return (double) $inputs.getItemCount();
    }
    
    final String get$3() {
        return Runtime_v2.stringFromString($inputs.getCustomerCategory());
    }
}

Second, it’s corresponding factory:

package org.formulacompiler.gen;
import org.formulacompiler.runtime.Computation;
import org.formulacompiler.runtime.ComputationFactory;
import org.formulacompiler.runtime.internal.Environment;
import org.formulacompiler.tutorials.QuickStart;

public final class $Factory
    implements ComputationFactory, QuickStart.PriceFinderFactory
{
    private final Environment $environment;
    
    public $Factory(Environment environment) {
        $environment = environment;
    }
    
    public final Computation newComputation(Object object) {
        return new $Root((QuickStart.LineItem) object, $environment);
    }
    
    public final QuickStart.PriceFinder newInstance
        (QuickStart.LineItem lineitem) {
        return new $Root(lineitem, $environment);
    }
}

The Runtime

Computations compiled by AFC are plain compiled Java classes. They rely only on a single small runtime library, formulacompiler-runtime.jar. So it’s good practice to simply save them to a .jar file and reuse them from there across application starts (or to even split the compiling application from the using application). However, they do require a special classloader. AFC therefore provides special methods to save and load engines properly.

First, let’s save our engine:

private void save( SaveableEngine engine ) throws Exception
{
  engine.saveTo( new FileOutputStream( "temp/test/CustomPriceFormula.jar" ) );
}

Loading it back is just as easy:

private Engine load() throws Exception
{
  return FormulaRuntime.loadEngine( new FileInputStream( "temp/test/CustomPriceFormula.jar" ) );
}

Again, add the following to the main method to make it runnable:

app.save( engine );

QuickStart app2 = new QuickStart();
Engine engine2 = app2.load();
PriceFinderFactory factory2 = app2.factoryFor( engine2 );
BigDecimal price2 = app2.compute( factory2 );
System.out.println( "The result is " + price2 );

Alright. Run it.

Summary

As promised, you just…

  • created a spreadsheet defining a customized line item price computation,
  • made AFC compile it to Java classes,
  • used those classes in your application,
  • looked at the generated code in Java source form, and
  • saved and reloaded the classes from a .jar file.

Hopefully, you also played around with the spreadsheet, compiling different formulas. You really should.

Not bad. Now learn more about the goals, the design, the API, and the supported functions of AFC.

Posted by 1010
01.JAVA/Java2012. 9. 20. 07:02
반응형

출처: http://realcool.egloos.com/3924002

 

엑셀의 함수를 자바로 구현할 일이 있어서 찾아본 결과

http://www.formulacompiler.org/doc/reference/index.htm

위의 사이트를 어렵게 찾았습니다.

위의 사이트에서 라이브러리 다운받으시고

필요한 엑셀함수를 호출해서 사용하면 됩니다.

하나만 예로 들어 만들어보면 다음과 같습니다.

엑셀의 chiinv를 예로 들면 다음과 같습니다.

import org.formulacompiler.runtime.internal.RuntimeDouble_v2;

/**
* 카이제곱분포
* @author realcool@empal.com
*
*/
public class Chiinv {
/**
* 카이제곱분포
* @param a
* @param b
* @return
*/
public double chiinv(double _x,double _degFreedom){
return RuntimeDouble_v2.fun_CHIINV(_x, _degFreedom);
}
final double NORMDIST(double input1,double input2,double input3,double input4) {
double d;
if (input3 <= 0.0) {
Runtime_v2.fun_ERROR("#NUM! because sigma <= 0 in NORMDIST");
d = (double) -1;
} else
d = (input4 != 0.0
? (Math.abs((input1 - input2) / input3
* 0.7071067811865476) < 0.7071067811865476
? (0.5
+ 0.5 * RuntimeDouble_v2.fun_ERF((input1 - input2)
/ input3
* 0.7071067811865476))
: (input1 - input2) / input3 * 0.7071067811865476 > 0.0
? 1.0 - 0.5 * (RuntimeDouble_v2.fun_ERFC
(Math.abs((input1 - input2) / input3
* 0.7071067811865476)))
: 0.5 * (RuntimeDouble_v2.fun_ERFC
(Math.abs((input1 - input2) / input3
* 0.7071067811865476))))
: (Math.exp((input1 - input2) * (input1 - input2)
/ (-2.0 * input3 * input3))
/ (2.5066282746310002 * input3)));
return d;
}
public static void main(String[] args){
Chiinv c = new Chiinv();
double d = c.chiinv(0.05, 2);
System.out.println(d);
}
}

Since AFC only supports Microsoft Excel spreadsheet semantics at the moment, this reference uses Excel as a baseline for expected behaviour of the supported functions.

Cells, Values, Names
Strings, numbers, dates, booleans; blank cells; cell names; ranges; numeric precision
Numeric Operators
%, *, +, -, /, <, <=, <>, =, >, >=, ^
Numeric Functions
ABS, ACOS, ACOSH, ASIN, ASINH, ATAN, ATAN2, ATANH, CEILING, COMBIN, COS, COSH, DEGREES, EVEN, EXP, FACT, FLOOR, GEOMEAN, HARMEAN, INT, LN, LOG, LOG10, MOD, ODD, PERMUT, PI, POWER, RADIANS, RAND, ROUND, ROUNDDOWN, ROUNDUP, SIGN, SIN, SINH, SQRT, TAN, TANH, TRUNC
Financial Functions
DB, DDB, FV, IRR, MIRR, NPER, NPV, PMT, PV, RATE, SLN, SYD, VDB
Statistical Functions
AVEDEV, BETADIST, BETAINV, BINOMDIST, CHIDIST, CHIINV, CHITEST, CONFIDENCE, CORREL, CRITBINOM, DEVSQ, EXPONDIST, FDIST, FINV, FISHER, FISHERINV, FORECAST, FTEST, GAMMADIST, GAMMAINV, GAMMALN, HYPGEOMDIST, INTERCEPT, KURT, LARGE, LOGINV, LOGNORMDIST, MEDIAN, MODE, NEGBINOMDIST, NORMDIST, NORMINV, NORMSDIST, NORMSINV, PEARSON, PERCENTILE, PERCENTRANK, POISSON, PROB, QUARTILE, RANK, RSQ, SKEW, SLOPE, SMALL, STANDARDIZE, STDEV, STDEVP, STDEVPA, STEYX, SUMX2MY2, SUMX2PY2, SUMXMY2, TDIST, TINV, TRIMMEAN, TTEST, VAR, VARA, VARP, WEIBULL, ZTEST
String Support
&, <, <=, <>, =, >, >=, CLEAN, CONCATENATE, EXACT, FIND, LEFT, LEN, LOWER, MATCH, MID, PROPER, REPLACE, REPT, RIGHT, SEARCH, SUBSTITUTE, TRIM, UPPER
Boolean Functions And IF
AND, IF, NOT, OR, true
Date Functions
DATE, DAY, DAYS360, HOUR, MINUTE, MONTH, NOW, SECOND, TIME, TODAY, WEEKDAY, YEAR
Conversion Functions
CHAR, CODE, DATEVALUE, DOLLAR, FIXED, N, ROMAN, T, TEXT, TIMEVALUE, VALUE
Type Functions
ISNONTEXT, ISNUMBER, ISTEXT
Aggregators
AVEDEV, AVERAGE, COUNT, COUNTA, COVAR, DEVSQ, KURT, MAX, MIN, PRODUCT, SKEW, STDEV, STDEVP, SUM, SUMIF, SUMSQ, VAR, VARP
Database Table Aggregators
COUNTIF, DAVERAGE, DCOUNT, DCOUNTA, DGET, DMAX, DMIN, DPRODUCT, DSTDEV, DSTDEVP, DSUM, DVAR, DVARP, SUMIF
Lookup Functions
CHOOSE, HLOOKUP, INDEX, LOOKUP, MATCH, VLOOKUP
Limitations
Things that are known to not work quite as expected yet.

Note On The Sample Expressions

To ensure its correctness, all the example expressions and their results have been cited from automated tests that are run with every release build.

이 글과 관련있는 글을 자동검색한 결과입니다 [?]

by 수평선 | 2009/01/18 19:54 | 자바코드 | 트랙백 | 덧글(2)

트랙백 주소 : http://realcool.egloos.com/tb/3924002
☞ 내 이글루에 이 글과 관련된 글 쓰기 (트랙백 보내기) [도움말]
Commented by 노태광 at 2009/03/11 11:07
정말 좋은 자료를 찾았어요. 너무 감사드립니다.
한가지 여쭤보고 싶은게 있는데요.
NORMDIST 함수를 사용하고 싶은데. 라이브러리에 포함되어 있지 않은 것 같아서요.
제가 잘 몰라서 못찾는 것 같은데. 좀 알려주시면 고맙겠습니다.^^*
Commented by 수평선 at 2009/03/23 10:26
찾으시는 자료가 혹시 다음일거라 생각되네요..
package JSci.maths.statistics;

import JSci.maths.*;

/**
* The NormalDistribution class provides an object for encapsulating normal distributions.
* @version 1.1
* @author Jaco van Kooten
*/
public final class NormalDistribution extends ProbabilityDistribution implements NumericalConstants {
private double mean,variance;
private double pdfDenominator,cdfDenominator;

/**
* Constructs the standard normal distribution (zero mean and unity variance).
*/
public NormalDistribution() {
this(0.0,1.0);
}
/**
* Constructs a normal distribution.
* @param mu the mean.
* @param var the variance.
*/
public NormalDistribution(double mu,double var) {
mean=mu;
if(var<=0.0)
throw new OutOfRangeException("The variance should be (strictly) positive.");
variance=var;
pdfDenominator=SQRT2PI*Math.sqrt(variance);
cdfDenominator=SQRT2*Math.sqrt(variance);
}
/**
* Constructs a normal distribution from a data set.
* @param array a sample.
* @author Mark Hale
*/
public NormalDistribution(double array[]) {
double sumX=array[0];
double sumX2=array[0]*array[0];
for(int i=1;i<array.length;i++) {
sumX+=array[i];
sumX2+=array[i]*array[i];
}
mean=sumX/array.length;
variance=(sumX2 - array.length*mean*mean)/(array.length-1);
pdfDenominator=SQRT2PI*Math.sqrt(variance);
cdfDenominator=SQRT2*Math.sqrt(variance);
}
/**
* Returns the mean.
*/
public double getMean() {
return mean;
}
/**
* Returns the variance.
*/
public double getVariance() {
return variance;
}
/**
* Probability density function of a normal (Gaussian) distribution.
* @return the probability that a stochastic variable x has the value X, i.e. P(x=X).
*/
public double probability(double X) {
return Math.exp(-(X-mean)*(X-mean)/(2*variance))/pdfDenominator;
}
/**
* Cumulative normal distribution function.
* @return the probability that a stochastic variable x is less then X, i.e. P(x&lt;X).
*/
public double cumulative(double X) {
return SpecialMath.complementaryError(-(X-mean)/cdfDenominator)/2;
}
/**
* Inverse of the cumulative normal distribution function.
* @return the value X for which P(x&lt;X).
*/
public double inverse(double probability) {
checkRange(probability);
if(probability==0.0)
return -Double.MAX_VALUE;
if(probability==1.0)
return Double.MAX_VALUE;
if(probability==0.5)
return mean;
// To ensure numerical stability we need to rescale the distribution
double meanSave=mean,varSave=variance;
double pdfDSave=pdfDenominator,cdfDSave=cdfDenominator;
mean=0.0;
variance=1.0;
pdfDenominator=Math.sqrt(TWO_PI);
cdfDenominator=SQRT2;
double X=findRoot(probability, 0.0, -100.0, 100.0);
// Scale back
mean=meanSave;
variance=varSave;
pdfDenominator=pdfDSave;
cdfDenominator=cdfDSave;
return X*Math.sqrt(variance)+mean;
}
}

http://jsci.sourceforge.net/api/JSci/maths/statistics/NormalDistribution.html
위 주소입니다.
http://jsci.sourceforge.net/

소스다운로드 해서 분석하면 될것 같네요..
Posted by 1010
01.JAVA/Java2012. 9. 15. 08:01
반응형

StackTraceElement STE = Thread.currentThread().getStackTrace()[1];

String className = STE.getClassName();
String methodName = STE.getMethodName());

 

Posted by 1010
01.JAVA/Java2012. 9. 15. 07:58
반응형
public class Test{
public static void main(String[] args){
String[] command = {"/bin/sh","-c","실행시킬 쉘 스크립트"};
int i;
try{
Process ps = Runtime.getRuntime().exec(command);
ps.waitFor();
System.out.println(ps.exitValue());
ps.destroy();
}catch(Exception e){
e.printStackTrace();
}
}
}

 

Posted by 1010
01.JAVA/Java2012. 5. 18. 01:19
반응형

Java 개발자를 위한 Dojo 개념

클래스 선언 및 컨텍스트 설정

Dave Draper, 웹스피어 앱 서버 관리 콘솔 개발자, IBM

요약:  Dojo는 웹 기반 애플리케이션에서 점점 더 많이 사용되고 있습니다. 개발자들은 대부분 강력한 Java™ 프로그래밍 기술을 갖고 있지만 Javascript에 대한 경험은 그다지 많지 않습니다. 개발자들은 유형이 강하게 지정된 오브젝트 지향 컴파일 언어로부터 유형이 약하게 지정된 동적 스크립팅 언어로 개념적 도약을 하기 위해 노력하고 있습니다. 이러한 혼란으로 인해 개발자들이 Dojo 클래스를 올바르게 선언하기는 어렵습니다. 이 기사에는 이러한 혼란을 정리하는 데 도움이 되는 정보가 있을 뿐만 아니라 Dojo가 컨텍스트를 설정하는 데 필요한 이유와 Dojo를 시작하는 방법이 기술되어 있습니다.

기사 게재일:  2011 년 12 월 14 일 
난이도: 중급 원문:  보기 PDF:  A4 and Letter (111KB)Get Adobe® Reader® 
페이지뷰:  1978 회 
의견:   0 (보기 | 의견 추가 - 로그인)

평균 평가 등급 0 개 총 0표 평균 평가 등급 (0 투표하기)
아티클 순위

소개

Javascript에 대한 경험이 거의 없거나 전혀 없는 상태에서 Dojo를 시작하려고 하는 Java 프로그래머라면 Dojo를 작동하게 하는 일부 개념을 이해하기 위해 노력하고 있을 가능성이 있다. 이 기사를 작성하는 현재, Dojo와 관련된 기본적인 문제는 Dojo가 여전히 초기 상태(버전 1.0이 2008년 2월에 릴리스되었을 뿐임)에 머물러 있고, 사용 가능한 문서도 다소 제한되어 있다는 점이다. 이 기사는 Java 코드와 Dojo 간의 격차를 메워서, 애플리케이션을 개발할 때 이 Dojo Toolkit을 사용하여 개발 속도를 신속하게 높일 수 있도록 도움을 준다.

Dojo Toolkit을 얻는 방법이나 이 도구를 사용하는 데 필요한 필수 명령문을 다루고 있는 자료는 많이 있기 때문에 이 기사에서는 이러한 정보를 다루지 않는다. 이 기사는 서블릿을 개발한 경험을 바탕으로 Dojo를 시작하려고 하는 웹 개발자를 대상으로 한다.

Javascript 해시

우선, Dojo 함수를 호출할 때(특히, "해시"나 Javascript 오브젝트를 사용할 때) 사용되는 구문을 이해해야 한다. 해시는 중괄호 사이에서 쉼표로 분리되는 속성 세트로 표현된다. 목록 1에 있는 간단한 예제에서는 6개의 속성(문자열, 정수형, 부울, 정의되지 않은 속성, 또 다른 해시 및 함수)으로 구성되는 해시를 선언하고 있다.


목록 1. Javascript 해시 예제
				
var myHash = {
    str_attr : "foo",
    int_attr : 7,
    bool_attr : true,
    undefined_attr : null,
    hash_attr : {},
    func_attr : function() {}
};			
                

Javascript는 유형이 느슨하게 지정되므로 각 속성이 속성의 이름에 링크된 값으로 초기화된다고 하더라도 초기 str_attr 속성이 순차적으로 정수나 부울 또는 기타 유형으로 설정될 수 있다. 해시에 있는 각 속성은 점 연산자를 사용하여 액세스하거나 설정할 수 있다(목록 2 참조).


목록 2. 해시 속성 액세스 및 설정
				
// Accessing a hash attribute...
console.log(myHash.str_attr);

// Setting a hash attribute...
myHash.str_attr = "bar";
                

myHash의 처음 네 가지 속성은 설명할 필요가 없을 것이다. 해시에는 해시 속성이 있을 수 있다는 사실은 당연하다. (이점은 프리미티브와 오브젝트를 모두 참조하는 Java 클래스와 유사하다고 생각될 수 있다.) 반드시 이해해야 하는 가장 중요한 속성은 마지막 속성이다.

함수는 오브젝트이다.

Java 코드에는 java.reflection.Method 클래스가 있지만, 본질적으로 이 클래스는 메소드에 대해 랩퍼로 작동한다. Javascript의 함수는 인수로서 다른 함수에 전달하고 참조하고 설정할 수 있는 기타 모든 것과 마찬가지로 오브젝트이다. 때로는 Java 메소드를 호출하는 과정에서 익명의 내부 클래스를 선언하듯이 함수를 호출하는 과정에서 함수를 새로 선언해야 한다.

Java 메소드와 Javascript 함수의 또 다른 중요한 차이점은 Javascript 함수는 다양한 컨텍스트에서 실행할 수 있다는 점이다. Java 프로그래밍에서는 키워드 this를 사용하여 함수가 사용된 클래스의 현재 인스턴스를 참조한다. Javascript 함수에서 사용되는 경우,this는 이 함수가 실행 중인 컨텍스트를 참조한다. 달리 지정하지 않는 한, 함수는 함수를 정의하는 클로저에서 실행된다.

간단히 말해서 클로저는 중괄호({}) 안에 포함된 모든 Javascript 코드라고 할 수 있다. Javascript 파일 안에서 선언된 함수는 this 키워드를 사용하여, 이 파일의 기본 본문에서 선언된 모든 변수를 액세스할 수 있지만, 조화를 이룰 다른 컨텍스트와 함께 함수를 제공되지 않는 한, 해시 안에서 선언된 함수에서는 this 키워드를 사용하여, 해시 안에서 선언된 변수만을 참조할 수 있다.

인클로즈된 함수가 Dojo 함수의 인수로 필요할 수도 있기 때문에 이러한 함수의 컨텍스트를 설정하는 방법을 이해하면 불필요한 디버깅을 많이 줄일 수 있다.

컨텍스트를 지정할 때 사용되는 기본적인 Dojo 함수는 dojo.hitch이다. dojo.hitch 함수를 결코 사용하지 않을 수도 있지만, 이 함수가 Dojo의 기본이 되는 함수이고 기타 여러 가지 함수가 은연 중에 이 함수를 호출하고 있다는 점을 이해해야 한다.

목록 3에는 컨텍스트 히칭의 작동 과정이 표시되어 있다(결과는 그림 1에 표시됨).

  • 변수가 글로벌 컨텍스트에서 정의되며(globalContextVariable), 또 다른 변수가 해시의 컨텍스트에서 선언된다(enclosedVariable).
  • accessGlobalContext() 함수는 globalContextVariable를 성공적으로 액세스하여 이 변수의 값을 표시한다.
  • 그러나, enclosedFunction() 함수는 로컬 변수(enclosedVariable)만을 액세스할 수 있다. (globalContextVariable의 값은 "undefined"로 표시된다는 점에 유의한다.)
  • dojo.hitch를 사용하여 enclosedFunction() 함수를 글로벌 컨텍스트로 "히치"하면 globalContextVariable 변수를 표시할 수 있다. 그러나 enclosedFunction() 함수가 실행 중인 컨텍스트에서 enclosedVariable를 선언하지 않았기 때문에 현재는 이 변수가 정의되지 않았다.

목록 3. 클로저 및 컨텍스트
				
var globalContextVariable = "foo";

function accessGlobalContext() {
    // This will successfully output "foo"...
    console.log(this.globalContextVariable);
};

var myHash = {
    enclosedVariable : "bar",
    enclosedFunction : function() {
                           // Display global context variable...
                           console.log(this.globalContextVariable);

                           // Display enclosed context variable...
                           console.log(this.enclosedVariable);
                       }
};

console.log("Calling accessGlobalContext()...");
accessGlobalContext();

console.log("Calling myHash.enclosedFunction()...");
myHash.enclosedFunction();

console.log("Switch the context using dojo.hitch...");
var switchContext = dojo.hitch(this, myHash.enclosedFunction);
switchContext();
            


그림 1. 컨텍스트 히칭의 작동 과정
컨텍스트 히칭의 작동 과정 

클래스 선언

클래스 선언 팁

  • myClass는 완전히 올바른 이름이지만, 완전한 클래스 이름 스타일(예: com.ibm.dojo.myClass)을 사용하여 이름을 선언하는 것이 좋다. 그렇다고 해서 이 클래스를 상대 경로 "./com/ibm/dojo/" 아래에 있는 파일 시스템에 배치해야 하는 것은 아니며, 이렇게 하는 것은 단지 클래스 간에 이름이 충돌할 가능성을 줄이기 위함이다.
  • 어떤 브라우저(Firefox)는 쉼표(,)를 무시고, 어떤 브라우저(Internet Explorer)는 제거하므로 마지막 속성 다음에는 쉼표가 없어야 한다. 이 규칙은 어느 위치에서나 해시 오브젝트를 선언할 때도 적용된다.

이 히칭이 중요한 이유는 Dojo 클래스를 선언하거나 자체 위젯을 작성하기 시작하면 분명해진다. Dojo의 기능 중 가장 우수한 것은dojo.connect 함수와 내장 pub/sub 모델을 사용하여 오브젝트를 서로 "연결"하는 기능이다.

클래스를 선언하려면 다음과 같은 세 가지 오브젝트가 필요하다.

  1. 클래스의 고유 이름
  2. 함수를 확장할 상위 클래스와 다중 상속을 시뮬레이션할 "혼합(mix-in)" 클래스
  3. 모든 속성과 함수를 정의하는 해시

목록 4에는 선언할 수 있는 가장 단순한 클래스가 표시되어 있고 목록 5에는 이 클래스를 인스턴스화하는 코드가 표시되어 있다.


목록 4. 기본적인 클래스 선언
				
dojo.declare(
   "myClass",
   null,
   {}
);


목록 5. 기본적인 클래스 인스턴스화
				
var myClassInstance = new myClass();

"진정한"(즉, 유용한) Dojo 클래스를 선언하고 싶으면 생성자를 이해해야 한다. Java 코드에서는 오버로드 생성자를 여러 개 선언하여 다양한 시그너처로 클래스를 인스턴스화할 수 있다. Dojo 클래스에서는 preambleconstructor 및 postscript를 선언할 수 있지만, 대부분의 클래스에서는 생성자만 선언하면 된다.

  • 생성자를 이용하면 constructor 인수가 확장된 클래스와 혼합된 클래스에 실제로 전달되기 전에 이 인수를 조작할 수 있으므로 다중 상속을 시뮬레이션하기 위해 다른 클래스를 혼합하지 않는 한, preamble을 선언할 필요는 없다.
  • postscript는 Dojo 위젯 라이프사이클 메소드를 드라이브하지만, 표준 Dojo 클래스에는 아무런 이점을 제공하지 않는다.

이들 중 어떤 것도 반드시 선언해야 하는 것은 아니지만, 해당 클래스의 인스턴스에 어떤 값을 전달하려면 최소한 constructor 함수는 선언해야 한다. 해당 클래스의 다른 인스턴스가 constructor 인수에 액세스하게 될 경우에는 선언된 속성에 이 인수를 지정해야 한다. 목록 6에는 constructor 인수 중 하나만을 클래스 속성에 지정한 후, 또 다른 메소드에서 두 인수를 모두 참조하는 클래스가 표시되어 있다.


목록 6. constructor 인수 지정
				
dojo.declare(
    "myClass",
    null,
    {
        arg1 : "",
        constructor : function(arg1, arg2) {
                          this.arg1 = arg1;
                      },
        myMethod : function() {
                       console.log(this.arg1 + "," + this.arg2);
                   }
    }
);

var myClassInstance = new myClass("foo", "bar");
myClassInstance.myMethod();
            


그림 2. constructor 인수를 지정한 결과
constructor 인수를 지정한 결과 

복합 속성 규칙

클래스 속성은 선언될 때 초기화되지만, 속성이 복합 오브젝트 유형(예: 해시 또는 배열)과 함께 초기화되는 경우, 이 속성은 Java 클래스의 public 정적 변수와 비슷해진다. 따라서 인스턴스가 속성을 업데이트할 때마다 변경된 속성이 기타 모든 인스턴스에 반영된다. 이러한 문제가 생기지 않도록 하려면 생성자에서 복합 속성을 초기화해야 하지만, 문자열, 부울 등과 같은 간단한 속성의 경우에는 반드시 이렇게 해야 할 필요는 없다.


목록 7. 글로벌 클래스 속성
				
dojo.declare(
    "myClass",
    null,
    {
        globalComplexArg : { val : "foo" },
        localComplexArg : null,
        constructor : function() {
                          this.localComplexArg = { val:"bar" };                          
                      }
    }
);

// Create instances of myClass A and B...
var A = new myClass();
var B = new myClass();

// Output A's attributes...
console.log("A's global val: " + A.globalComplexArg.val); 
console.log("A's local val: " + A.localComplexArg.val); 

// Update both of A's attributes...
A.globalComplexArg.val = "updatedFoo";
A.localComplexArg.val = "updatedBar";

// Update B's attributes...
console.log("A's global val: " + B.globalComplexArg.val);
console.log("A's local val: " + B.localComplexArg.val);
            


그림 3. 클래스 속성
클래스 속성 

메소드 오버라이드

수퍼클래스 메소드는 동일한 이름으로 속성을 선언하여 확장할 수 있다. Javascript는 예기치 않은 인수를 무시하고 누락된 인수를 모두 널로 대체하기 때문에 오버로드 개념이 없다. Java 코드에서는 오버로드된 메소드를 호출하기 위해 수퍼클래스 메소드(즉,super().methodName(arg1, arg1);)를 호출하지만, Dojo에서는 상속된 메소드(this.inherited(arguments);)를 사용한다. 목록 8에는 선언된 두 가지 클래스가 표시되어 있으며, 여기에서는 child가 parent를 확장하고 helloWorld 메소드를 확장하지만, inherited를 호출하여 parent의 함수에 액세스한다.


목록 8. Dojo에서 수퍼클래스 메소드 호출
	
dojo.declare(
    "parent",
    null,
    {
        helloWorld : function() {
                         console.log("parent says 'hello world'");
                     }
    }
);

dojo.declare(
    "child",
    parent,
    {
        helloWorld : function() {
                         this.inherited(arguments); // Call superclass method...
                         console.log("child says 'hello world'");
                     }
    }
);

var child = new child();
child.helloWorld();


그림 4. Dojo에서 수퍼클래스 메소드를 호출한 결과
Dojo에서 수퍼클래스 메소드를 호출한 결과 

메소드 컨텍스트 설정

목록 9에는 인스턴스화되는 즉시, 제공된 문자열 배열의 요소를 문자열 ArrayList에 복사하는 Java 클래스가 표시되어 있다. 목록 10에 있는 코드를 사용하여 같은 기능을 Dojo로 제공하는 것이 합리적이다. (constructor 함수에서 targetArray를 인스턴스화하면 targetArray가 글로벌화되는 것을 막을 수 있다.) 불행히도 dojo.forEach 메소드에서 선언된 함수는 함수의 본문을 참조하여 this를 정의하는 클로저를 작성하기 때문에 그림 5와 같은 오류 메시지가 표시된다.


목록 9. Java 코드에서 클래스 범위 지정 변수 액세스
	
import java.util.ArrayList;

public class MyClass
{
    // Declare an ArrayList of Strings...
    private ArrayList<String> targetArray = new ArrayList<String>();

    public MyClass(String[] sourceArray)
    {
        // Copy each element of a String[] into the ArrayList...
        for (String val: sourceArray) 
        {
            this.targetArray.add(val);
        }
    }
}


목록 10. Dojo에서 컨텍스트 누락
	
dojo.declare(
    "myClass",
    null,
    {
        targetArray: null,
        constructor: function(source) {
                         // Initialise in constructor to avoid making global
                         this.targetArray = []; 

                         // Copy each element from source into target...
                         dojo.forEach(source, 
                                    function(item) {
                                        this.targetArray[this.targetArray.length] = item;
                                    });
                     },
    }
);

// This will cause an error!
var myClass = new myClass(["item1","item2"]);


그림 5. Dojo에서 컨텍스트가 누락된 결과
Dojo에서 컨텍스트가 누락된 결과 

targetArray가 함수로 둘러싸인 컨텍스트에서 정의되어 있지 않다고 하더라도 인수를 사용하여 targetArray가 정의되어 있는 컨텍스트를 Dojo 함수에 전달할 수 있다. 따라서 이 컨텍스트에서 선언된 모든 오브젝트(함수 포함)를 this 키워드로 액세스할 수 있다. 목록 11에는 올바르게 구현된 결과가 표시되어 있다. (굵은체로 된 추가 코드에 주목한다.)


목록 11. Dojo에서 올바른 컨텍스트 설정
	
dojo.declare(
    "myClass",
    null,
    {
        targetArray: null,
        constructor: function(source) {
                         // Initialise in constructor to avoid making global
                         this.targetArray = []; 

                         // Copy each element from source into target...
                         dojo.forEach(source, 
                                    function(item) {
                                        this.targetArray[this.targetArray.length] = item;
                                    }, this);
                     },
    }
);
                

언제나 컨텍스트가 Dojo 함수 시그너처에서 동일한 인수로 전달되는 것은 아니다.

  • dojo.subscribe에서는 컨텍스트가 함수가 선언되기 에 전달된다(목록 12).
  • dojo.connect에서는 트리거 메소드가 정의되어 있는 컨텍스트와 대상 메소드가 정의되어 있는 컨텍스트를 모두 제공해야 한다. 목록 13에는 예제가 표시되어 있으며 여기에서 obj1은 methodA가 정의되어 있는 컨텍스트이고 obj2는 methodB가 정의되어 있는 컨텍스트이다. obj1에서 methodA를 호출하면 obj2에서 methodB가 호출된다.

목록 12. dojo.subscribe로 컨텍스트 설정
	
dojo.declare(
    "myClass",
    null,
    {
        subscribe : function() {
                        dojo.subscribe("publication",
                                       this, 
                                       function(pub) { 
                                           this.handlePublication(pub);
                                       });
                    },

        handlePublication : function(pub) {
                                console.log("Received: " + pub);
                            }
    }
);
                


목록 13. dojo.connect로 컨텍스트 설정
	
dojo.connect(obj1, "methodA", obj2, "methodB");

결론

더 구조화된 Java 코드 환경에 익숙해진 개발자에게는 Javascript가 결코 자연스럽지 않을 것이다. 그러나 클래스 선언 기능과 관련해서 Dojo 구현은 클라이언트 측 개발이 훨씬 더 수월해질 정도로 발전했다. 컨텍스트 자체와 컨텍스트를 설정하는 시점 및 방법을 잘 이해하면 Java 개발자들이 겪는 어려움을 많이 줄일 수 있을 뿐만 아니라 그들이 자신 있게 자신의 도구 상자에 Javascript를 추가하게 될 수 있을 것이다.


참고자료

  • 시작하는 데 필요한 모든 정보와 자료는 DojoToolkit.org에서 찾을 수 있다.

  • Meet the JavaScript Development Toolkit(developerWorks, May 2008) developerWorks 기사에서 Javascript를 작성하는 데 도움이 되는 Eclipse 기반 도구에 관해 자세히 알아보자.

  • 기술 서점에서 다양한 기술 주제와 관련된 서적을 살펴보자. 

  • 기타 Ajax 기술(Dojo 포함)에 관한 자세한 정보는 developerWorks Ajax 자원 센터에서 확인할 수 있다.

  • Dojo API의 완전한 참조를 얻을 수 있다. 

  • 우수한 Dojo 코딩 예제는 Dojo campus에서 이용할 수 있다.

필자소개

Dave Draper

Dave Draper는 6년 동안 WebSphere Application Server Administrative Console을 개발해 왔다. 그는 Sun에서 인증한 웹 컴포넌트 개발자로, 웹 기반 도구를 개발하는 데 폭넓은 경험을 갖고 있다.

Posted by 1010
01.JAVA/Java2012. 5. 8. 15:03
반응형

출처 : http://5dol.tistory.com/182

우선 간단히 확인하면 아래처럼 나온다. jstat -옵션 -pid -시간 하면 된다.
ex) ~]jstat -gc 16543 1000

저건 pid 16543(java)를 1초에 한번씩 결과값을 보여달란거다. 

pid는 ps -efw | grep java로 알아내면 된다. 

~] jstat --help

 invalid argument count
Usage: jstat -help|-options
       jstat -<option> [-t] [-h<lines>] <vmid> [<interval> [<count>]]

Definitions:
  <option>      An option reported by the -options option
  <vmid>        Virtual Machine Identifier. A vmid takes the following form:
                     <lvmid>[@<hostname>[:<port>]]
                Where <lvmid> is the local vm identifier for the target
                Java virtual machine, typically a process id; <hostname> is
                the name of the host running the target Java virtual machine;
                and <port> is the port number for the rmiregistry on the
                target host. See the jvmstat documentation for a more complete
                description of the Virtual Machine Identifier.
  <lines>       Number of samples between header lines.
  <interval>    Sampling interval. The following forms are allowed:
                    <n>["ms"|"s"]
                Where <n> is an integer and the suffix specifies the units as
                milliseconds("ms") or seconds("s"). The default units are "ms".
  <count>       Number of samples to take before terminating.
  -J<flag>      Pass <flag> directly to the runtime system.



옵션은 다음과 같다.

 옵션명내용 
 class 클래스 로더의 동작에 관한 통계 데이터
 compiler HotSpot Just-in-Time 컴파일러의 동작에 관한 통계 데이터
 gc 가베지 컬렉트된 heap의 동작에 관한 통계 데이터
 gccapactiy 세대마다의 용량과 대응하는 영역에 관한 통계 데이터
 gccause 가베지 콜렉션 통계 데이터의 개요 (-gcutil 와 같다)와 직전 및 현재 (적용 가능한 경우)의 가베지 콜렉션 이벤트의 원인
 gcnew New 세대의 동작에 관한 통계 데이터
 gcnewcapacity New 세대의 사이즈와 대응하는 영역에 관한 통계 데이터
 gcold Old 세대 및 Permanent 세대의 동작에 관한 통계 데이터
 gcoldcapacity Old 세대의 사이즈에 관한 통계 데이터
 gcpermcapacity Permanent 세대의 사이즈에 관한 통계 데이터
 gcutil 가베지 콜렉션 통계 데이터의 개요
 printcompilationHotSpot 컴파일 방법의 통계 데이터

각 옵션에 대한 컬럼에 대한 설명은 다음과 같다.

-class  클래스 로더의 통계 데이터
 Loaded Bytes Unloaded Bytes Time
 로드 된 클래스의 수 로드 된 K 바이트수 언로드된 클래스의 수 언로드된 K 바이트수 클래스의 로드나 언로드 처리에 필요로 한 시간

-compiler HotSpot Just-In-Time 컴파일러의 통계 데이터
 Compiled Failed Invalid Time FailedType FailedMethod
 실행된 컴파일 태스크의 수 실패한 컴파일 태스크의 수 무효로 된 컴파일 태스크의 수 컴파일 태스크의 실행에 필요로 한 시간 마지막에 실패한 컴파일의 컴파일 타입 마지막에 실패한 컴파일의 클래스명과 메소드

-gc 가비지 컬렉트된 heap의 통계 데이터
 컬럼명 설명
 S0C Survivor 영역 0 의 현재의 용량 (KB)
 S1C Survivor 영역 1 의 현재의 용량 (KB)
 S0U Survivor 영역 0 의 사용율 (KB)
 S1U Survivor 영역 1 의 사용율 (KB)
 EC Eden 영역의 현재의 용량 (KB)
 EU Eden 영역의 사용율 (KB)
 OC Old 영역의 현재의 용량 (KB)
 OU Old 영역의 사용율 (KB)
 PC Permanent 영역의 현재의 용량 (KB)
 PU Permanent 영역의 사용율 (KB)
 YGC Young 세대의 GC 이벤트수
 YGCT Young 세대의 가베지 콜렉션 시간
 FGC 풀 GC 이벤트수
 FGCT 풀 가베지 콜렉션 시간
 GCT 가베지 콜렉션의 합계 시간

-gccapacity 메모리프르 세대 및 영역 용량

 컬럼명설명 
 NGCMN New 세대의 최소 용량 (KB)
 NGCMX New 세대의 최대 용량 (KB)
 S0C Survivor 영역 0 의 현재의 용량 (KB)
 S1C Survivor 영역 1 의 현재의 용량 (KB)
 EC Eden 영역의 현재의 용량 (KB)
 OGCMN Old 세대의 최소 용량 (KB)
 OGCMX Old 세대의 최대 용량 (KB)
 OGC Old 세대의 현재의 용량 (KB)
 OC Old 영역의 현재의 용량 (KB)
 PGCMN Permanent 세대의 최소 용량 (KB)
 PGCMX Permanent 세대의 최대 용량 (KB)
 PGC Permanent 세대의 현재의 용량 (KB)
 PC Permanent 영역의 현재의 용량 (KB)
 YGC Young 세대의 GC 이벤트수
 FGC 풀 GC 이벤트수
 NGC New 세대의 현재의 용량 (KB)

-gcutil 가베지 콜렉션 통계 데이터의 개요
 컬럼명 설명
 S0 Survivor 영역 0 의 사용율 (현재의 용량에 대한 퍼센티지)
 S1 Survivor 영역 1 의 사용율 (현재의 용량에 대한 퍼센티지)
 E Eden 영역의 사용율 (현재의 용량에 대한 퍼센티지)
 O Old 영역의 사용율 (현재의 용량에 대한 퍼센티지)
 P Permanent 영역의 사용율 (현재의 용량에 대한 퍼센티지)
 YGC Young 세대의 GC 이벤트수
 YGCT Young 세대의 가베지 콜렉션 시간
 FGC 풀 GC 이벤트수
 FGCT 풀 가베지 콜렉션 시간
 GCT 가베지 콜렉션총시간

-gccause GC 이벤트를 포함한 가베지 콜렉션 통계 데이터(gcutil에 두개 컬럼이 추가됨)
 컬럼명 설명
 LGCC 마지막 가베지 콜렉션의 원인
 GCC 현재의 가베지 콜렉션의 원인

-gcnew New 세대의 통계 데이터
 컬럼명 설명
 S0C Survivor 영역 0 의 현재의 용량 (KB)
 S1C Survivor 영역 1 의 현재의 용량 (KB)
 S0U Survivor 영역 0 의 사용율 (KB)
 S1U Survivor 영역 1 의 사용율 (KB)
 TT 전당 들어가 귀의치
 MTT 최대 전당 들어가 귀의치
 DSS 적절한 Survivor 사이즈 (KB)
 EC Eden 영역의 현재의 용량 (KB)
 EU Eden 영역의 사용율 (KB)
 YGC Young 세대의 GC 이벤트수
 YGCT Young 세대의 가베지 콜렉션 시간

-gcold 옵션 Old 및 Permanent 세대의 통계 데이터
 컬럼명설명 
 PC Permanent 영역의 현재의 용량 (KB)
 PU Permanent 영역의 사용율 (KB)
 OC Old 영역의 현재의 용량 (KB)
 OU Old 영역의 사용율 (KB)
 YGC Young 세대의 GC 이벤트수
 FGC 풀 GC 이벤트수
 FGCT 풀 가베지 콜렉션 시간
 GCT 가베지 콜렉션총시간

-gcoldcapacity Old 세대의 통계 데이터
 컬럼명설명 
 OGCMN Old 세대의 최소 용량 (KB)
 OGCMX Old 세대의 최대 용량 (KB)
 OGC Old 세대의 현재의 용량 (KB)
 OC Old 영역의 현재의 용량 (KB)
 YGC Young 세대의 GC 이벤트수
 FGC 풀 GC 이벤트수
 FGCT 풀 가베지 콜렉션 시간
 GCT 가베지 콜렉션총시간

-printcompilation HotSpot 컴파일 방법의 통계 데이터

 컬럼명설명 
 Compiled 실행된 컴파일 태스크의 수
 Size 메소드의 바이트 코드의 바이트수
 Type 컴파일 타입
 Method 컴파일 방법을 특정하는 클래스명과 메소드명. 클래스명에서는, 이름 공간의 단락 문자로서 「.」(은)는 아니고 「/」이 사용된다. 메소드명은, 지정된 클래스내의 메소드이다. 이러한 2 개의 필드의 형식은, HotSpot -XX:+PrintComplation 옵션과 대응하고 있다


사용 방법

jstat -gcutil -h5 16543 1000 10

jstat를 -gcutil 옵션을 주고 5개씩 출력마다 헤더를 출력(h5)하고 1초마다 출력하되 10개씩 보여준다.

Posted by 1010
01.JAVA/Java2012. 5. 8. 15:01
반응형


1. jstat 수행 방법
   - 우선 모니터링 하고자 하는 프로세스의 ID 를 확인합니다.
      확인하는 방법은 ps -ef | grep java 로 확인을 해도 되고, 프롬프트 상태에서 jps 라고 입력한 뒤 엔터를 치면 해당 JVM에서 수행된 프로세스의 ID를 보여줍니다.
      (jps 사용시에는 PATH에 해당 JDK가 설정되어 있어야 합니다.
 
 

  - 두번째로 jstat 명령을 수행 합니다.

      jstat -gcutil -h20 -t 7251 3000 3000

      -> gcutil : gcutil 에 대해서 수행

      -> -h20 : 20라인마다 header 찍음

      -> -t : time stamp 프린트(JVM 이 스타트 된 이후의 시간)

      -> 7251 : 프로세스 id

      -> 3000 : interval (ms 단위)

      -> 3000 : count
 
 - 수행 결과
 
2. 각각의 항목 설명

  - S0 Survivor 영역 0 의 사용율 (현재의 용량에 대한 퍼센티지)

  - S1 Survivor 영역 1 의 사용율 (현재의 용량에 대한 퍼센티지)

  - Eden 영역의 사용율 (현재의 용량에 대한 퍼센티지)

  - Old 영역의 사용율 (현재의 용량에 대한 퍼센티지)

  - Permanent 영역의 사용율 (현재의 용량에 대한 퍼센티지)

  - YGC Young 세대의 GC 이벤트수

  - YGCT Young 세대의 가베지 콜렉션 시간

  - FGC 풀 GC 이벤트수

  - FGCT 풀 가베지 콜렉션 시간

  - GCT : 가베지 콜렉션총시간
 
3. 샘플
  >jstat -gcutil 21891 250 7
      S0     S1     E      O      P     YGC    YGCT    FGC    FGCT     GCT
     12.44   0.00  27.20   9.49  96.70    78    0.176     5    0.495    0.672
     12.44   0.00  62.16   9.49  96.70    78    0.176     5    0.495    0.672
     12.44   0.00  83.97   9.49  96.70    78    0.176     5    0.495    0.672
      0.00   7.74   0.00   9.51  96.70    79    0.177     5    0.495    0.673
      0.00   7.74  23.37   9.51  96.70    79    0.177     5    0.495    0.673
      0.00   7.74  43.82   9.51  96.70    79    0.177     5    0.495    0.673
      0.00   7.74  58.11   9.51  96.71    79    0.177     5    0.495    0.673

 

    이 예의 출력은, Young 세대의 콜렉션이 3 번째와 4 번째의 샘플간에 행해진 것을 나타내고 있습니다.

    콜렉션에는 0.001 초 걸리고 있어 오브젝트가 Eden 영역 (E)으로부터 Old 영역 (O)에 승격했기 때문에,

    Old 영역의 사용율은 9.49% 에서 9.51% 에 증가하고 있습니다.

    Survivor 영역은, 콜렉션전은 12.44% 가 사용되고 있었습니다만, 콜렉션 후는 7.74% 밖에 사용되고 있지 않습니다.

 

실시간으로 메모리 사용을 좀 확인해야 하는 상황에서는 위와 같이 jstat 로 간단하게 모니터링을 수행하면

현재의 JVM 메모리 사용 상황을 확인이 가능할 것 같습니다.

 

가장 정확한 건 GC 로그를 별도의 파일로 출력하게 해서 분석하는 것이지만, 이럴 경우에는 실시간으로 확인이 힘들기 때문에

위와 같이 사용하는 것도 괜찮은 방법 중의 하나일 것 같습니다.

물론 JVM 에 계속 request 를 보내기 때문에 부하가 있을 듯 하지만, 지금 생각으로는 크게 영향은 미치지 않을 듯 하네요..

영향이 고려되면 interval을 좀 조정하든지 하면 될 듯 합니다.

Posted by 1010
01.JAVA/Java2012. 5. 8. 15:00
반응형

JVM을 사용하여 디렉토리 프록시 서버에 대한 모니터링된 데이터 검색

디렉토리 프록시 서버는 JVM(Java Virtual Machine) 내에서 실행되고 JVM 시스템의 메모리에 따라 달라집니다. 디렉토리 프록시 서버가 올바르게 실행되고 있는지 확인하려면 JVM 시스템의 메모리 사용을 모니터링해야 합니다.

JVM 시스템의 매개 변수를 조정하는 방법에 대한 자세한 내용은 Sun Java System Directory Server Enterprise Edition 6.0 Deployment Planning Guide의 Hardware Sizing For Directory Proxy Server을 참조하십시오.

기본적으로 JVM 시스템의 힙 크기는 250MB입니다. 디렉토리 프록시 서버에 물리적 메모리가 충분하지 않은 경우 힙 크기는 250MB보다 작을 수 있습니다.

디렉토리 프록시 서버가 실행 중인 경우 JVM 시스템의 힙 크기를 모니터링하여 메모리가 부족해지지 않도록 할 수 있습니다. 이렇게 하려면 JDK(Java Development Kit)와 함께 제공되는 표준 도구를 사용합니다. 이러한 도구는 $JAVA_HOME/bin/jps 및 $JAVA_HOME/bin/jstat 디렉토리에 있습니다.

ProcedureJVM의 힙 크기를 보려면

DSCC를 사용하여 이 작업을 수행할 수 없습니다. 이 절차에 설명된 것처럼 명령줄을 사용하십시오.

    JVM의 힙 크기를 봅니다.


    $ dpadm get-flags instance-path jvm-args
    jvm-args: -Xms250M  -Xmx250M

Procedure디렉토리 프록시 서버가 실행 중인 경우 JVM의 힙 크기를 모니터링하려면

DSCC를 사용하여 이 작업을 수행할 수 없습니다. 이 절차에 설명된 것처럼 명령줄을 사용하십시오.

  1. 디렉토리 프록시 서버 인스턴스의 PID를 봅니다.


    $ jps
  2. JVM 시스템에 사용된 메모리를 봅니다.


    $ jstat -gcutil PID
    
    • 0 열이 거의 100%에 도달하면 JVM 시스템에 메모리가 부족한 것입니다.

    • FGC는 전체 가비지 컬렉션(GC) 이벤트 수입니다. 가비지 컬렉션은 광범위합니다.

    • GCT(Garbage Collection Time)는 GC에 사용된 시간입니다.

Posted by 1010
01.JAVA/Java2012. 5. 8. 14:59
반응형

BigAdmin System Administration Portal
Java SE 6 플랫폼 응용 프로그램 모니터링 및 관리
Print-friendly VersionPrint-friendly Version

번역 책임의 한계: 
본 기사는 어느 정도의 인간의 간섭과 사후 편집을 포함하여 컴퓨터 소프트웨어 프로그램으로 번역되었습니다. 번역은 독자의 편리함을 위해 “있는 그대로” 제공되며, Sun은 번역된 문장 또는 페이지 전체로서의 정확성 또는 완전성에 대한 아무런 대표성이 없으며 어떠한 책임도 지지 않습니다. 사이트와 툴의 사용에 관한 추가 고지 사항에 대해서는 본 사이트의 이용 약관을 참조하십시오.

 
 

기사 색인

응용 프로그램이 원래 속도보다 느리게 실행되거나 이전보다 느리게 실행되는 듯 하거나 또는 응용 프로그램이 응답이 없거나 중단되었습니다. 프로덕션 환경 또는 개발 중에 이러한 상황에 처할 수 있습니다. 이러한 문제의 원인은 무엇일까요? 흔히 메모리 누출, 교착 상태 및 동기화 문제와 같은 원인은 진단하기 어렵습니다. Java Platform Standard Edition(Java SE) 버전 6은 즉시 사용할 수 있는 모니터링 및 관리 기능을 제공하여 여러 일반적인 Java SE 문제를 진단할 수 있도록 도와줍니다.

이 기사는 Java SE 6 응용 프로그램을 모니터링 및 관리하는 단기 과정입니다. 첫 번째, Java SE 응용 프로그램의 일반적인 문제와 증상에 대해 설명합니다. 두 번째, Java SE 6의 모니터링 및 관리 기능의 개요에 대해 설명합니다. 세 번째, 다양한 JDK(Java Development Kit) 도구를 사용하여 이러한 문제를 진단하는 방법에 대해 설명합니다.

주: Java SE 플랫폼 사양에 대한 API 추가 또는 기타 개선 사항은 JSR 270 전문가 그룹에 의해 검토 및 승인됩니다.

Java SE 응용 프로그램의 일반적인 문제

일반적으로 Java SE 응용 프로그램의 문제는 메모리, 스레드, 클래스 및 잠금과 같은 중요 자원과 관련되어 있습니다. 자원 경합 또는 누수는 성능 문제나 예상치 않은 오류로 이어질 수 있습니다. 표 1은 Java SE 응용 프로그램의 몇 가지 일반적인 문제와 증상을 요약하고 개발자가 각 문제의 원인을 진단하는 데 도움을 줄 수 있는 도구를 나열합니다.

표 1. 일반적인 문제 진단 도구
 
 
문제
증상
진단 도구
OutOfMemoryError
메모리 사용 증가
빈번한 가비지 컬렉션
 
성장률이 높은 클래스
예상치 않은 수의 인스턴스가 있는 클래스
메모리 맵(jmap)
jmap -histo 옵션 참조
 
객체가 의도하지 않게 참조되고 있음
jconsole 또는 jmap(jhat 포함)
jmap -dump 옵션 참조
객체가 종료 대기 중임
jconsole
jmap -dump(jhat 포함)
객체 모니터의 스레드 블록 또는 java.util.concurrent 잠금
스레드 CPU 시간이 지속적으로 증가
jconsole(JTop 포함)
치열한 경합 통계가 있는 스레드
jconsole

메모리 부족

JVM(Java Virtual Machine)*에는 비힙 및 기본 유형의 메모리가 있습니다.

힙 메모리는 모든 클래스 인스턴스 및 배열이 할당되는 메모리의 런타임 데이터 영역입니다. 비힙 메모리에는 JVM의 최적화 또는 내부 처리에 필요한 메소드 영역과 메모리가 포함되어 있습니다. 이는 런타임 상수 풀, 필드 및 메소드 데이터, 메소드 및 구성자의 코드와 같은 클래스당 구조를 저장합니다. 기본 메모리는 운영 체제에 의해 관리되는 가상 메모리입니다. 응용 프로그램을 할당하기에 메모리가 부족한 경우 java.lang.OutOfMemoryError가 발생합니다.

다음은 각 메모리 유형에서의 OutOfMemoryError에 대한 가능한 오류 메시지입니다.

  • 힙 메모리 오류. 응용 프로그램이 새 객체를 만들지만 힙이 충분한 공간을 가지지 않고 더 이상 확장할 수 없을 경우 다음 오류 메시지와 함께 OutOfMemoryError가 발생합니다.

    java.lang.OutOfMemoryError: Java heap space
     
  • 비힙 메모리 오류. 영구 생성은 인턴된 문자열뿐만 아니라 클래스당 구조를 저장하는 핫스폿 VM 구현에 있는 비힙 메모리 영역입니다. 영구 생성이 가득 차면 응용 프로그램이 클래스 로드나 인턴된 문자열 할당에 실패하여 다음 오류 메시지와 함께 OutOfMemoryError가 발생합니다.

    java.lang.OutOfMemoryError: PermGen space
    
     
  • 기본 메모리 오류. 응용 프로그램의 JNI(Java Native Interface) 코드 또는 기본 라이브러리 및 JVM 구현은 기본 힙으로부터 메모리를 할당합니다. OutOfMemoryError는 기본 힙에서 할당이 실패할 경우 발생합니다. 예를 들어, 다음 오류 메시지는 스왑 공간이 부족함을 나타냅니다. 이 오류는 운영 체제의 구성 문제 또는 시스템의 다른 프로세스에 의해 발생할 수 있으며 많은 메모리를 소비합니다.

    java.lang.OutOfMemoryError: request <size> bytes for <reason>. 
    Out of swap space?
    
     

메모리 부족 문제는 구성(해당 응용 프로그램이 실제로 그렇게 많은 메모리를 필요로 함) 문제 또는 메모리 사용을 줄이기 위한 프로파일링 및 최적화를 필요로 하는 응용 프로그램의 성능 문제 때문일 수 있습니다. 메모리 사용을 줄이기 위한 메모리 설정 구성 및 응용 프로그램 프로파일링은 이 기사의 범위를 넘어서는 것이지만 관련 정보에 대해 핫스폿 VM 메모리 관리 백서(PDF)를 참조하거나 NetBeans IDE Profiler와 같은 프로파일링 도구를 사용할 수 있습니다.

메모리 누출

JVM은 자동 메모리 관리를 책임지고 있으며 이는 응용 프로그램에 사용되지 않은 메모리를 재생 이용합니다. 그러나 응용 프로그램이 더 이상 필요하지 않은 객체에 대한 참조를 유지하는 경우 객체는 가비지 컬렉션될 수 없으며 객체가 제거될 때까지 힙에서 공간을 차지합니다. 이러한 의도하지 않은 객체 유지를 메모리 누출이라고 합니다. 응용 프로그램이 많은 양의 메모리를 누출하면 결국에는 메모리를 다 사용하여 OutOfMemoryError가 발생합니다. 또한 가비지 컬렉션은 응용 프로그램이 여유 공간을 만들려고 함에 따라 더욱 더 빈번히 발생하므로 응용 프로그램의 실행 속도가 느려집니다.

종료자

OutOfMemoryError의 또 다른 가능한 원인은 종료자의 과도한 사용입니다. java.lang.Object 클래스에는 finalize라는 보호된 메소드가 있습니다. 클래스는 이 finalize메소드를 대체하여 해당 클래스의 객체가 가비지 컬렉션에 의해 재생 이용되기 전에 시스템 자원을 지우거나 정리를 수행할 수 있습니다. 객체에 대해 호출될 수 있는 finalize메소드는 해당 객체의 finalizer입니다. 종료자가 실행되는 시기 또는 적어도 실행은 될지 여부가 보장되지 않습니다. 종료자가 있는 객체는 종료자가 실행될 때까지 가비지 컬렉션되지 않습니다. 따라서 종료 대기 중인 객체는 응용 프로그램에 의해 더 이상 참조되지 않더라도 메모리를 유지하여 메모리 누출과 유사한 문제가 발생할 수 있습니다.

교착 상태

교착 상태는 둘 이상의 스레드가 각기 다른 스레드가 잠금을 해제하도록 기다릴 때 발생합니다. Java 프로그래밍 언어는 모니터를 사용하여 스레드를 동기화합니다. 각 객체는 모니터와 연관되어 있어 객체 모니터라고도 할 수 있습니다. 스레드가 객체에서 synchronized 메소드를 호출하는 경우 해당 객체는 잠깁니다. 동일한 객체에서synchronized 메소드를 호출하는 또 다른 스레드는 잠금이 해제될 때까지 차단됩니다. 내장 동기화 지원 외에도 J2SE 5.0에 도입된 java.util.concurrent.locks 패키지는 조건을 잠금고 기다리는 프레임워크를 제공합니다. 교착 상태는 java.util.concurrent 잠금뿐만 아니라 객체 모니터와도 연관될 수 있습니다.

일반적으로 교착 상태는 응용 프로그램 또는 응용 프로그램의 일부가 응답하지 않도록 만듭니다. 예를 들어, GUI(그래픽 사용자 인터페이스) 업데이트를 책임지고 있는 스레드가 교착 상태에 빠지면 GUI 응용 프로그램이 중단되고 어떠한 사용자 동작에도 응답하지 않습니다.

루핑 스레드

루핑 스레드는 또한 응용 프로그램이 중단되도록 만들 수 있습니다. 하나 이상의 스레드가 무한 루프에서 실행되고 있는 경우 해당 루프는 사용 가능한 모든 CPU 주기를 다 소비하고 응용 프로그램의 나머지 부분이 응답하지 않도록 만들 수 있습니다.

치열한 잠금 경합

동기화는 다중 스레드 응용 프로그램에 많이 사용되어 공유 자원에 대한 액세스를 상호 배제하고 다중 스레드 간의 작업을 조정 및 완료합니다. 예를 들어, 응용 프로그램이 객체 모니터를 사용하여 데이터 구조에서 업데이트를 동기화합니다. 두 스레드가 동시에 데이터 구조를 업데이트하려고 하면 한 스레드만이 객체 모니터를 획득하여 데이터 구조 업데이트를 진행할 수 있습니다. 한편, 다른 스레드는 첫 번째 스레드가 업데이트를 완료하고 객체 모니터를 해제할 때까지 synchronized 블록에 들어가기 위해 기다리므로 차단됩니다. 동기화 경합은 응용 프로그램 성능 및 확장성에 영향을 줍니다.

Java SE 6 플랫폼의 모니터링 및 관리 기능

Java SE 6의 모니터링 및 관리 지원에는 다양한 VM(가상 머신) 자원을 검사하기 위한 유용한 진단 도구와 프로그램 인터페이스가 포함되어 있습니다. 프로그램 인터페이스에 대한 자세한 내용은 API 사양을 참조하십시오.

JConsole은 Java 모니터링 및 관리 콘솔로서 이를 사용하여 런타임에 다양한 VM 자원 사용을 모니터링할 수 있습니다. 또한 응용 프로그램 실행 동안 이전 섹션에서 설명한 증상을 감시할 수 있습니다. JConsole을 사용하여 동일한 시스템에서 로컬로 실행되거나 다른 시스템에서 원격으로 실행되는 응용 프로그램에 연결하여 다음과 같은 정보를 모니터링할 수 있습니다.

  • 메모리 사용 및 가비지 컬렉션 작업
  • 스레드 상태, 스레드 스택 추적 및 잠금
  • 종료 대기 중인 객체의 수
  • 가동 시간 및 프로세스가 소비하는 CPU 시간과 같은 런타임 정보
  • JVM에 대한 입력 인수 및 응용 프로그램 클래스 경로와 같은 VM 정보

또한 Java SE 6에는 기타 명령줄 유틸리티가 포함되어 있습니다. jstat 명령은 메모리 사용, 가비지 컬렉션 시간, 클래스 로딩 및 JIT(Just-In-Time) 컴파일러를 비롯한 다양한 VM 통계를 인쇄합니다. jmap 명령를 사용하면 런타임에 힙 막대 그래프 및 힙 덤프를 얻을 수 있습니다. jhat 명령을 사용하면 힙 덤프를 분석할 수 있습니다. 또한 jstack 명령을 사용하면 스레드 스택을 추적할 수 있습니다. 이러한 진단 도구들은 특수 모드에서 시작할 필요 없이 모든 응용 프로그램에 연결할 수 있습니다.

JDK 도구를 사용한 진단

이 섹션에서는 JDK 도구를 사용하여 일반적인 Java SE 문제를 진단하는 방법에 대해 설명합니다. JDK 도구를 사용하면 응용 프로그램에 대한 보다 정확한 진단 정보를 얻을 수 있으며 응용 프로그램이 원래대로 작동하고 있는지 알 수 있습니다. 일부 경우에 진단 정보는 문제를 진단하고 근본 원인을 식별하기에 충분할 수 있습니다. 다른 경우에는 프로파일링 도구나 디버거를 사용하여 문제를 디버깅해야 할 수 있습니다.

각 도구에 대한 자세한 내용은 Java SE 6 도구 설명서를 참조하십시오.

메모리 누출 진단 방법

메모리 누출은 재현에 매우 긴 시간이 소요될 수 있으며 매우 드물거나 모호한 조건에서 발생하는 경우 특히 그렇습니다. 이상적인 경우는 개발자가 OutOfMemoryError가 발생하기 전에 메모리 누출을 진단하는 것입니다.

먼저 JConsole을 사용하여 메모리 누출이 계속해서 증가하는지 모니터링합니다. 이는 가능한 메모리 누출을 나타냅니다. 그림 1은 메모리 사용이 증가하고 있는 MemLeak라는 응용 프로그램에 연결된 JConsole의 메모리 탭을 보여줍니다. 메모리 탭 내에 삽입된 상자에서 GC(가비지 컬렉션) 작업을 관찰할 수 있습니다.

그림 1: 메모리 탭은 증가하는 메모리 사용을 보여주고 있으며 이는 가능한 메모리 누출을 나타냅니다.
그림 1: 메모리 탭은 증가하는 메모리 사용을 보여주고 있으며 이는 가능한 메모리 누출을 나타냅니다.

또는 다음과 같이 jstat 명령을 사용하여 메모리 사용 및 가비지 컬렉션 통계를 모니터링할 수 있습니다.

  $ <JDK>/bin/jstat -gcutil <pid> <interval> <count>

jstat -gcutil 옵션은 <count> 횟수 동안 지정된 표본 추출 <interval>의 각 표본에서 프로세스 ID <pid>의 실행 응용 프로그램의 힙 사용 및 가비지 컬렉션의 요약을 인쇄합니다. 이는 다음과 같은 표본 출력을 생성합니다.

  S0     S1     E      O      P     YGC   YGCT    FGC    FGCT     GCT
0.00 0.00 24.48 46.60 90.24 142 0.530 104 28.739 29.269
0.00 0.00 2.38 51.08 90.24 144 0.536 106 29.280 29.816
0.00 0.00 36.52 51.08 90.24 144 0.536 106 29.280 29.816
0.00 26.62 36.12 51.12 90.24 145 0.538 107 29.552 30.090

jstat 출력 및 다양한 VM 통계를 얻기 위한 기타 옵션에 대한 자세한 내용은 jstat 설명서 페이지를 참조하십시오.

힙 막대 그래프

응용 프로그램에 메모리 누출이 있는 것으로 의심되는 경우 jmap 명령을 사용하면 총 인스턴스 수 및 각 클래스의 인스턴스가 사용한 총 바이트 수를 비롯한 클래스당 통계를 보여주는 힙 막대 그래프를 얻을 수 있습니다. 다음 명령줄을 사용합니다.

$ <JDK>/bin/jmap -histo:live <pid>
 

힙 막대 그래프 출력은 다음과 유사합니다.

num   #instances    #bytes  class name
--------------------------------------
  1:    100000    41600000  [LMemLeak$LeakingClass;
  2:    100000     2400000  MemLeak$LeakingClass
  3:     12726     1337184  <constMethodKlass>
  4:     12726     1021872  <methodKlass>
  5:       694      915336  [Ljava.lang.Object;
  6:     19443      781536  <symbolKlass>
  7:      1177      591128  <constantPoolKlass>
  8:      1177      456152  <instanceKlassKlass>
  9:      1117      393744  <constantPoolCacheKlass>
 10:      1360      246632  [B
 11:      3799      238040  [C
 12:     10042      160672  MemLeak$FinalizableObject
 13:      1321      126816  java.lang.Class
 14:      1740       98832  [S
 15:      4004       96096  java.lang.String
 < more .....>
 

jmap -histo 옵션은 프로세스 ID <pid>의 실행 응용 프로그램의 힙 막대 그래프를 요청합니다. jmap이 힙의 라이브 객체만 카운트하도록 live 하위 옵션을 지정할 수 있습니다. 연결할 수 없는 객체를 비롯한 모든 객체를 카운트하려면 다음 명령줄을 사용합니다.

$ <JDK>/bin/jmap -histo <pid>
 

두 힙 막대 그래프(하나는 연결할 수 없는 객체를 비롯한 모든 객체를 카운트 하는 그래프, 다른 하나는 라이브 객체만 카운트하는 그래프)를 비교하여 어떤 객체가 가비지 컬렉션될 것인지 확인하는 것이 유용한 경우가 있을 수 있습니다. 하나 이상의 힙 막대 그래프 스냅샷에서 일반적으로 다음과 같은 특성을 지니는 메모리 누출이 있을 수 있는 클래스를 식별할 수 있습니다.

  • 인스턴스가 예상치 않게 많은 양의 메모리를 사용함
  • 클래스의 인스턴스 수가 시간이 지남에 따라 높은 속도로 증가함
  • 가비지 컬렉션될 것으로 예상한 클래스 인스턴스가 가비지 컬렉션되지 않음

jmap 유틸리티를 사용하여 얻은 이전의 힙 막대 그래프가 LeakingClass 및 그 배열이 가장 많은 인스턴스 수를 가지는 것으로 나타나 메모리 누출이 의심됩니다.

힙 막대 그래프는 일부 경우 메모리 누출을 진단하는 데 필요한 정보를 제공합니다. 예를 들어, 응용 프로그램이 소수의 몇 군데에서만 누출 클래스를 사용하는 경우 소스 코드에서 쉽게 누출을 찾을 수 있습니다. 반면에 java.lang.String 클래스와 같이 응용 프로그램에서 누출 클래스가 널리 사용되는 경우 객체에 대한 참조를 추적하고 힙 덤프를 분석하여 심도 있게 진단해야 합니다.

힙 덤프

다음 방법을 통해 힙 덤프를 얻을 수 있습니다. 첫 번째 방법은 다음 명령줄을 통해 jmap 명령을 사용하여 힙 덤프를 얻는 것입니다.

$ <JDK>/bin/jmap -dump:live,file=heap.dump.out,format=b <pid>
 

이는 다음과 같은 표본 출력을 생성합니다.

Dumping heap to d:\demo\heap.dump.out ...
Heap dump file created
 

jmap -dump 옵션은 프로세스 ID <pid>의 실행 응용 프로그램의 힙 덤프가 지정된 파일 이름 heap.dump.out에 작성되도록 요청합니다. -histo 옵션과 유사하게 live 하위 옵션은 선택 사항이며 라이브 객체만이 덤프되도록 지정합니다.

두 번째 방법은 그림 2에 나타난 것처럼 HotSpotDiagnostic MBean의 dumpHeap 연산을 호출하여 JConsole에서 힙 덤프를 얻는 것입니다.

그림 2: HotSpotDiagnostic MBean의 <code>dumpHeap</code> 연산을 호출하여 JConsole에서 힙 덤프를 얻습니다.
그림 2: HotSpotDiagnostic MBean의 dumpHeap 연산을 호출하여 JConsole에서 힙 덤프를 얻습니다.
 

이는 JConsole을 사용하여 응용 프로그램을 모니터링할 때 특히 유용하고 편리한데 그 이유는 하나의 도구를 사용하여 모니터링 및 문제 해결을 수행할 수 있기 때문입니다. 또한 JConsole을 사용하여 응용 프로그램에 원격으로 연결할 수 있으므로 또 다른 시스템에서 힙 덤프를 요청할 수 있습니다.

지금까지 런타임에 힙 덤프를 얻을 수 있는 두 가지 방법에 대해 살펴보았습니다. HeapDumpOnOutOfMemoryError HotSpot VM 옵션을 설정하여 OutOfMemoryError가 처음 발생할 때 힙 덤프를 작성하도록 요청할 수도 있습니다. 응용 프로그램을 시작할 때 명령줄에 이 옵션을 설정할 수 있습니다.

$ <JDK>/bin/java -XX:+HeapDumpOnOutOfMemoryError ... 
 

다음과 같이 jinfo 명령을 사용하여 응용 프로그램이 실행되고 있는 동안 이 옵션을 설정할 수도 있습니다.

$ <JDK>/bin/jinfo -flag +HeapDumpOnOutMemoryError <pid>
 

마지막으로, 그림 3에 나타난 것처럼 HotSpotDiagnostic MBean의 setVMOption 연산을 호출하여 JConsole에서 HeapDumpOnOutOfMemoryError 옵션을 설정할 수 있습니다.

그림 3: HotSpotDiagnostic MBean의 <code>setVMOption</code> 연산을 호출하여 VM 옵션을 설정합니다.
그림 3: HotSpotDiagnostic MBean의 setVMOption 연산을 호출하여 VM 옵션을 설정합니다.
 

OutOfMemoryError가 발생하면 java_pid<pid>.hprof라는 이름의 힙 덤프 파일이 자동으로 작성됩니다.

java.lang.OutOfMemoryError: Java heap space
Dump heap to java_pid1412.hprof ...
Heap dump file created [68354173 bytes in 4.416 secs ]
Exception in thread "main" java.lang.OutOfMemoryError: Java heap space
at MemLeak.consumeMemory(MemLeak.java:25)
at MemLeak.main(MemLeak.java:6)
 

힙 분석

힙 덤프 파일이 작성되면 jhat 명령을 사용하여 힙 분석을 수행하고 어떤 참조가 누출이 의심되는 객체를 활성 상태로 유지하는지 확인할 수 있습니다.

$ <JDK>/bin/jhat heap.dump.out
 

이는 다음과 같은 표본 출력을 생성합니다.

Reading from heap.dump.out...
Dump file created Tue Jul 20 12:05:59 PDT 2006
Snapshot read, resolving...
Resolving 283482 objects...
Chasing references, expect 32 dots..........................................
Eliminating duplicate references............................................
Snapshot resolved.
Started HTTP server on port 7000
Server is ready.
 

jhat 유틸리티(이전의 HAT 힙 분석 도구)는 힙 덤프를 읽고 지정된 포트에서 HTTP 서버를 시작합니다. 그런 다음 브라우저를 사용하여 서버에 연결하고 지정된 힙 덤프에 대해 쿼리를 실행합니다. 그림 4는 jhat가 분석하는 힙 덤프의 모든 클래스(java.* 및 javax.* 제외)를 보여줍니다. 이 도구는 다음을 비롯한 여러 쿼리를 지원합니다.

  • 주어진 객체에 설정된 루트의 모든 참조 경로 표시. 이 쿼리는 메모리 누출을 찾는데 특히 유용합니다.
  • 모든 클래스에 대한 인스턴스 카운트 표시
  • 모든 클래스에 대한 인스턴스 카운트 및 크기를 비롯한 힙 막대 그래프 표시
  • 종료자 요약 표시
그림 4: 힙 덤프는 <code>java.*</code> 및 <code>javax.*</code>를 제외한 모든 클래스를 보여줍니다.
그림 4: 힙 덤프는 java.* 및 javax.*를 제외한 모든 클래스를 보여줍니다.
 

내장 OQL(Object Query Language) 인터페이스를 통해 사용자 정의 쿼리를 개발하여 특정 문제를 드릴 다운할 수도 있습니다. 예를 들어, 문자열 길이가 100 이상인 모든java.lang.String 객체를 찾으려면 OQL query 페이지에서 다음 쿼리를 입력합니다.

select s from java.lang.String s where s.count >= 100
 

과도한 종료자 사용 진단 방법

과도한 종료자 사용은 메모리를 유지하고 응용 프로그램이 해당 메모리를 신속하게 재생 이용하는 것을 방해합니다. 이러한 과도한 사용은 OutOfMemoryError를 유발할 수 있습니다. 그림 5가 나타낸 것처럼 JConsole을 사용하여 종료 대기 중인 객체 수를 모니터링할 수 있습니다.

그림 5: JConsole의 VM 탭은 종료 대기 중인 객체 수를 보여줍니다.
그림 5: JConsole의 VM 탭은 종료 대기 중인 객체 수를 보여줍니다.
 

앞에서 설명한 것처럼 jhat를 사용하여 힙 덤프에 어떤 종료 가능한 객체가 있는지도 확인할 수 있습니다.

또한 Solaris 및 Linux 운영 체제에서 jmap 유틸리티를 사용하여 종료 가능한 객체의 클래스를 확인할 수 있습니다.

$ <JDK>/bin/jmap -finalizerinfo <pid>
 

교착 상태 진단 방법

Java SE 6은 응용 프로그램에 교착 상태가 발생했는지 확인할 수 있는 매우 편리한 두 가지 방법을 제공하며 java.util.concurrent 잠금을 지원하도록 교착 상태 감지 기능이 향상되었습니다. JConsole 및 jstack 명령은 객체 모니터에 관련된 교착 상태 즉, synchronized 키워드 또는 java.util.concurrent 소유 가능한 동기화를 사용하여 얻은 잠금을 찾을 수 있습니다.

그림 6은 Deadlock 응용 프로그램에 2개의 교착 상태가 있음을 보여주고, Deadlock 2 탭은 객체 모니터에 차단된 3개의 교착 상태 스레드를 보여줍니다. 각 교착 상태 탭은 교착 상태에 관련된 스레드 목록을 보여주고 스레드가 차단된 잠금을 식별하며 해당 잠금을 소유한 스레드를 가리킵니다.

그림 6: JConsole이 2개의 교착 상태를 감지하고 세부 내용을 제공합니다.
그림 6: JConsole이 2개의 교착 상태를 감지하고 세부 내용을 제공합니다.
 

jstack 유틸리티를 사용하여 스레드 덤프를 얻고 교착 상태를 감지할 수도 있습니다.

$ <JDK>/bin/jstack <pid>
 

다음은 java.util.concurrent 소유 가능한 동기화에 관련된 교착 상태 1개를 감지하는 표본 jstack 출력의 아래 부분입니다.

Sample jstack output
더 큰 표본을 보려면 여기를 누르십시오.

루핑 스레드 진단 방법

CPU 사용 증가는 루핑 스레드의 징조 중 하나입니다. JTop은 응용 프로그램의 스레드당 CPU 사용 시간을 보여주는 JDK 데모입니다. JTop은 CPU 사용량별로 스레드를 정렬하여 CPU 시간을 과도하게 사용하고 있는 스레드를 쉽게 감지할 수 있게 해줍니다. 높은 스레드 CPU 소비가 예상된 동작이 아닌 경우 해당 스레드는 루핑일 수 있습니다.

JTop을 독립 실행형 GUI로 실행할 수 있습니다.

$ <JDK>/bin/java -jar <JDK>/demo/management/JTop/JTop.jar
 

또는 JConsole 플러그인으로 실행할 수 있습니다.

$ <JDK>/bin/jconsole -pluginpath <JDK>/demo/management/JTop/JTop.jar 
 

그림 7에 나타난 것처럼 응용 프로그램의 각 스레드가 사용하고 있는 CPU 시간을 보여주는 추가 JTop 탭을 통해 JConsole 도구를 시작합니다. JTop 탭은 LoopingThread가 많은 양의 CPU 시간을 사용하고 있고 사용량이 점점 증가하고 있음을 보여주며 따라서 루핑 스레드가 의심됩니다. 개발자는 이 스레드의 소스 코드를 검토하여 무한 루프가 포함되어 있는지 확인해야 합니다.

그림 7: JTop 탭은 응용 프로그램의 각 스레드가 얼마의 CPU 시간을 사용하는지 보여줍니다.
그림 7: JTop 탭은 응용 프로그램의 각 스레드가 얼마의 CPU 시간을 사용하는지 보여줍니다.

치열한 잠금 경합 진단 방법

어떤 잠금이 병목 현상인지 판단하는 것은 꽤 어려울 수 있습니다. JDK는 잠금 경합에 소비된 총 누적 시간뿐만 아니라 스레드가 객체 모니터에서 차단되거나 대기한 횟수와 같은 스레드당 경합 통계를 제공합니다. 그림 8에 나타난 것처럼 스레드가 객체 모니터에서 차단되거나 대기한 횟수에 대한 정보는 JConsole의 스레드 탭에 표시된 스레드 정보에서 항상 확인할 수 있습니다.

그림 8: 스레드 탭은 스레드가 객체 모니터에서 차단되거나 대기한 횟수를 보여줍니다.
그림 8: 스레드 탭은 스레드가 객체 모니터에서 차단되거나 대기한 횟수를 보여줍니다.
 

그러나 경합에 소비된 총 누적 시간을 추적하는 기능은 기본적으로 비활성화되어 있습니다. 그림 9에 나타나 것처럼 Threading MBean의ThreadContentionMonitoringEnabled 속성을 true로 설정하여 스레드 경합 시간의 모니터링을 활성화할 수 있습니다.

그림 9: Threading MBean의 <code>ThreadContentionMonitoringEnabled</code> 속성을 설정하여 스레드 경합의 모니터링을 활성화합니다.
그림 9: Threading MBean의 ThreadContentionMonitoringEnabled 속성을 설정하여 스레드 경합의 모니터링을 활성화합니다.
 

스레드 경합 통계를 확인하여 스레드에서 기대보다 치열한 잠금 경합이 발생했는지 판단할 수 있습니다. 그림 10에 나타난 것처럼 스레드 ID를 입력 인수로 사용하여 Threading MBean의 getThreadInfo 연산을 호출하여 스레드가 차단된 총 누적 시간을 얻을 수 있습니다.

그림 10: Threading MBean의 <code>getThreadInfo</code> 연산의 반환값입니다.
그림 10: Threading MBean의 getThreadInfo 연산의 반환값입니다.
 
요약

Java SE 6 플랫폼은 프로덕션 및 개발 환경에서 Java SE 응용 프로그램의 일반적인 문제를 진단할 수 있는 몇 가지 모니터링 및 관리 도구를 제공합니다. JConsole을 사용하면 응용 프로그램을 관찰하여 증상을 확인할 수 있습니다. 또한 JDK 6에는 몇 가지 기타 명령줄 도구도 포함되어 있습니다. jstat 명령은 메모리 사용 및 가비지 컬렉션 시간과 같은 다양한 VM 통계를 인쇄합니다. jmap 명령을 사용하면 런타임에 힙 막대 그래프 및 힙 덤프를 얻을 수 있습니다. jhat 명령을 사용하면 힙 덤프를 분석할 수 있습니다. 또한jstack 명령을 사용하면 스레드 스택을 추적할 수 있습니다. 이러한 진단 도구들은 특수 모드에서 시작할 필요 없이 모든 응용 프로그램에 연결할 수 있습니다. 이러한 도구들을 사용하여 응용 프로그램의 문제를 보다 효율적으로 진단할 수 있습니다.

추가 정보
저자 정보

Mandy Chung은 Sun Microsystems의 Java SE 모니터링 및 관리 팀장이며, java.lang.management API, 즉시 사용할 수 있는 원격 관리, JConsole 및기타 HotSpot VM 서비스가용성 기술을 개발하고 있습니다. 저자의 블로그를 방문하십시오.


* "Java Virtual Machine" 및 "JVM"이라는 용어는 Java 플랫폼용 가상 머신을 의미합니다.

 Java SE 6 빌드 95 이하 버전을 사용하는 경우 dumpHeap 연산은 첫 번째 인수만을 사용하며 라이브 객체만을 덤프합니다.

Posted by 1010
01.JAVA/Java2012. 1. 27. 14:50
반응형

jar 파일 생성 시 MANIFEST.MF 작성

JAVA 2009/03/05 08:56
파일 위치 및 명령어 실행 위치 현황
D:\Java_EXE \
└Genealogy2 \ ←명령어 실행 위치
└data
└lib ← swt.jar 위치
META-INF (folder)
└src ← 실행 파일 위치(package)

● META-INF 폴더는 root 폴더 바로 아래에 만든다.
( src 등 기타 위치에 생성하는 건 의미없다.
eclipse에서 export를 통해 jar를 만들 때 root 폴더가 아닌 곳에 만든 manifest.mf를 참조할 경우
eclipse가 root 폴더 아래 새로 만들기 때문에 실행 파일 설정이 정상적으로 이뤄지지 않는다. )

root\META-INF 밑에 MANIFEST.MF 만들어서 아래 내용 추가하고, Export 할 때 manifest.mf를 추가해 준다. (eclipse에서 작업해야 Resource is out of sync ~~~ 안 뜬다. 뜨면 한 번 열어서 읽어 준다.)

MANIFEST.MF 파일 내용
Manifest-Version: 0.1
Class-Path: lib/swt.jar
Main-Class: src.ManageGenealogy


Class-Path: lib/swt.jar ← jar 파일 내부 폴더에 둔다.
그 외 참조할 class가 있으면 그 위치를 적어준다. 구분자는 ' '
이 예제의 경우, swt.jar 를 제외한 모든 class가 메인 클래스와 같은 src 하부에 있어서 추가없음.
Class-Path는 72자까지만 됨.
Class-Path는 외부 경로(local)만 가능함. Jar 내부나, Internet 등으로 접근하는 Jar는 참조할 수 없음.
(-> 내가 만든 jar 안에 내가 참조할 외부 jar를 넣고, Class-Path에 잡아주어도 참조하지 못한다.
참조하려면 1. 참조할 jar를 풀어서 내 jar 안에 넣고, sealed 처리하던가
2. 외부에 참조할 jar를 위치시키고 class-path에 경로를 잡아준다.

Main-Class: src.ManageGenealogy ← 실행 파일 경로 및 파일 명
jar 파일을 실행 가능하게 만든다.
실행 명령어 : java -jar Genealogy.jar

※ 주의사항 :
1. MANIFEST.MF 내에서 다른 class, directory, jar를 참조할 때는 경로 정보를 입력할 때 '/'를 사용한다.
(경우에 따라서 '\'를 쓰게 되는 경우도 있으니 아래 Exception 발생 시 경로를 수정해 볼 것.
Exception in thread "main" java.lang.NoClassDefFoundError: aaa/bbb/ccc )
2. 각 'attirbute :' 다음에 공백 한 칸 둘 것. (ex 'Class-Path: ')
3. Main-Class attribute 다음에 carrage return(엔터) 하나 둘 것. 안 그러면 실행 시 main class를 인식 못 함.

참조 : http://www.ibm.com/developerworks/kr/library/j-jar/index.html
http://java.sun.com/docs/books/tutorial/deployment/jar/basicsindex.html
http://blog.naver.com/echris7?Redirect=Log&logNo=140012585340
관련 글 : http://digicom.tistory.com/165
---------------------------------------------------------------------------------------------------

...


Posted by 1010
01.JAVA/Java2011. 10. 29. 16:15
반응형

serialVersionUID 기본 알고리즘

자바에서 serial version uid를 생성하는 것은 기본적으로 서로 다른 클래스들 간의 구별을 하기 위한 것이다.
동일한 이름을 가진 클래스라 하더라도 메소드나 필드가 다를 경우 서로 다른 것으로 인식하는 것이 기본이기 때문에 Object Serialization을 할 때 import/export 등에서 버전에 따라 종종 불일치 에러가 발생하는 것을 만나게 된다.

클래스가 바뀌었다는 것을 기본 serial version uid 계산 알고리즘을 통해서 검출했기 때문이다.
물론
private static final long serialVersionUID = -6120832682080437368L;
와 같이 클래스에 직접 serial version uid 값을 지정해버리면 기본 uid 계산 알고리즘을 사용하지 않고 이 값을 사용하게 되므로 버전에 따른 불일치 에러는 막을 수 있다.

이렇게 하지 않은 경우 사소한 메소드 시그너처 변경으로도 불일치가 발생하게 된다.

기본 uid 값 계산에 사용되는 정보들은 다음과 같다.

1. 클래스 이름 (fully qualified)
2. 클래스의 접근 제한자 (public, final, abstract, 또 interface 여부)
3. 각 멤버 필드의 시그너처 (이름과 접근 제한자, 타입)
4. 각 멤버 메소드의 시그너처 (이름과 접근 제한자, 각 인자별 정보, 리턴 타입)
4. 각 생성자의 시그너처 (접근 제한자, 각 인자별 정보)
5. static initializer block 존재 유무

이러한 값들을 사용하여 적당한 문자열을 만든 다음 SHA 알고리즘을 사용하여 해시값을 계산한 값이 기본 UID 값이 된다.
이때 필드나 메소드, 생성자의 선언 순서는 바뀌더라도 상관없도록 sort를 한다음 계산을 한다.

여기에서 알 수 있듯이 사소한 변경만으로도 기본 suid 값은 변경되게 마련이다.
따라서 serializable 객체로 객체 통신에 사용되는 클래스들은 가능하면 명시적으로 suid 값을 지정해주는 것이 버전 관리의 문제를 피할 수 있는 가장 깨끗한 방법이다.


출처 : http://logonjava.blogspot.com/2006/05/serialversionuid.html





serialversionutil-civan.zip

데이타의 전송은 객체건 뭐건 결국 바이트의 흐름으로 전송하게 된다. 저장도 그렇고...
다시 읽었을 때 객체의 자료구조를 그대로 보존하지 않으면 않된다.(일명 Serialzation)
자바에선 Serializable 인터페이스만 구현하면 알아서 이를 보장해주기에 단지 implements Serializable 만 추가하면 된다.

일전에도 언급한 적이 있지만 5.0 이후부터는 Serialzable 인터페이스를 구현한 클래스에서
static final long 타입의 serialVersionUID 상수를 선언하라는 경고문구를
이클립스의 노란 warning 아이콘과 더불어 확인 할 수 있다.

만일 serialVersionUID를 지정하지 않으면 실행시점에서 JVM이 디폴트 값을 산정하게 되며,
그 알고리즘은 Java(TM) Object Serialization Specification 정의된 것을 따른다고 한다.
한마디로 굳이 신경 쓸필요는 없다는 뜻이고 이클립스내에서 이 경고아이콘을 제외하도록 설정할 수도 있다.

그러나 모든 serialization이 필요한 클래스에는 명시적으로 serialVersionUID를 선언해줄것을 강력하게 권유하고 있는데
그 이유는 디폴트 serialVersionUID 계산은 클래스의 세부 사항을 매우 민감하게 반영하기 때문에 컴파일러 구현체에 따라서 달라질 수 있어 deserialization(serialization 했던 객체를 복구하는 과정)과정에서 예상하지 못한 InvalidClassExceptions을 유발할 수 있다.
라는 것이 그 이유란다.

즉 서로 다른 자바 컴파일러 구현체 사이에서도 동일한 serialVersionUID값을 얻기 위해서는 명시적으로 serialVersionUID값을 선언해야 하며 가능한 serialVersionUID을 private으로 선언하라는 것이다.
(상속되어 쓰여지는 것은 유용하지 않고, 해당 클래스에서만 쓰일 것이기 때문에....)

이궁 이렇게 이야기하는데 한줄 더 써주지 머...
ㅋ 말은 이리 하지만 무진장 귀찮다. serialver.exe를 이용하면 된다는데 다른 건 없나 찾아보게 되었다

첨부파일을 압축을 풀고 이클립스 플러그인 디렉토리에 넣어두면 자동생성 플러그인 설치는 끝
3.3에서도 이상없이 작동한다. 사용방법은 다음과 같다.
파일을 선택 마우스 오른클릭하면
Add serialVersionUID 라는 메뉴가 추가 되어 있는것을 확인할 수 있다.

그러면 다음과 같이 클래스에 serialVersionUID가 자동 생성되었슴을 확인할 수 있다.

간단히 private 만 적어주면 끝...



출처 : http://blog.daum.net/_blog/BlogTypeView.do?blogid=0LU4x&articleno=5216095#ajax_history_home



이클립스를 쓰다보면 코드엔 이상이 없는데 왠 노란색 warining 표시가 쭈욱 표시되어 있다.

표시되는것이 눈에 거슬려도 빨간색 Error도 아니고 실행엔 문제가 없기에 무시하고 넘어가곤 했는데 알고 지나가는거랑 모르고 지나는건 틀리기에 함 살펴보기로 했다


메시지를 살펴보니

warning: [serial] serializable class <CLASSNAME> has no definition of serialVersionUID 라고 되어 있다.


해석하면 직렬화 가능 클래스에서 long 유형의 static final serialVersionUID 필드를 선언하지 않습니다 라는데 serialVersionUID? 이게 머지? 이런걸 쓰이는 곳이 없는데...


알아보니 java beans 객체는 serialVersionUID 를 가지게 되는데 이것은 또한

jvm 에서 특정 객체를 인식하기 위한 UID 이기도 하다라는걸 찾게 되었다.


1.4까지는 java beans 에서 serialVersionUID를 명시하지 않아도 JVM에서 serialVersionUID 를 제네레이션 해서 관리하고 있지만 1.5 부턴 serialVersionUID를 명시하길 권고하고 있다고 하니 생각해볼 문제인듯(머 권고사항이라고 하니 기냥 무시할까나..-_-)


해결방법은 serialVersionUID 은 롱 타입의 값으로 다음과 같이 유일한 값을 지정해주면된다.

private static final long serialVersionUID = 1222179582713735628L;


단순히 이클립스 내에서 해당 warning메시지를 표시하지 않기를 원한다면

Window -> Preferences -> Java -> Compiler -> Errors/Warnings ->
Potential programming problems -> Serializable class without
serialVersionUID -> Ignore.


한글 언어팩 적용시에는
창 -> 환경설정 -> Java -> 오류/경고 -> 잠재적 프로그래밍 문제점 ->
serialVersionUID가 없는 직렬화 가능 클래스 -> 무시


를 통해서 해당 메시지를 표시하지 않게 설정할 수 있다.


---------------------------------------------------------------------------------------------

데이타의 전송은 객체건 뭐건 결국 바이트의 흐름으로 전송하게 된다. 저장도 그렇고...
다시 읽었을 때 객체의 자료구조를 그대로 보존하지 않으면 않된다.(일명 Serialzation)
자바에선 Serializable 인터페이스만 구현하면 알아서 이를 보장해주기에 단지 implements Serializable 만 추가하면 된다.
일전에도 언급한 적이 있지만 5.0 이후부터는 Serialzable 인터페이스를 구현한 클래스에서
static final long 타입의 serialVersionUID 상수를 선언하라는 경고문구를
이클립스의 노란 warning 아이콘과 더불어 확인 할 수 있다.
만일 serialVersionUID를 지정하지 않으면 실행시점에서 JVM이 디폴트 값을 산정하게 되며,
그 알고리즘은 Java(TM) Object Serialization Specification 정의된 것을 따른다고 한다.
한마디로 굳이 신경 쓸필요는 없다는 뜻이고 이클립스내에서 이 경고아이콘을 제외하도록 설정할 수도 있다.
그러나 모든 serialization이 필요한 클래스에는 명시적으로 serialVersionUID를 선언해줄것을 강력하게 권유하고 있는데
그 이유는 디폴트 serialVersionUID 계산은 클래스의 세부 사항을 매우 민감하게 반영하기 때문에 컴파일러 구현체에 따라서 달라질 수 있어 deserialization(serialization 했던 객체를 복구하는 과정)과정에서 예상하지 못한 InvalidClassExceptions을 유발할 수 있다.
라는 것이 그 이유란다.
즉 서로 다른 자바 컴파일러 구현체 사이에서도 동일한 serialVersionUID값을 얻기 위해서는 명시적으로 serialVersionUID값을 선언해야 하며 가능한 serialVersionUID을 private으로 선언하라는 것이다.
(상속되어 쓰여지는 것은 유용하지 않고, 해당 클래스에서만 쓰일 것이기 때문에....)


class의 warning이 뜬 부분을 클릭 해보면 [ + Add generated serial version ID ] 나타난다 클릭하면
eclipse에서 알아서 생성 해준다.

[출처]
java warning: no definition of serialVersionUID |작성자 시반
Posted by 1010
01.JAVA/Java2011. 7. 15. 21:21
반응형

Apache Commons를 활용한 예
http://blog.naver.com/phrack?Redirect=Log&logNo=80086347596

Struts Taglib을 활용한 예
http://blog.naver.com/phrack?Redirect=Log&logNo=80053722854

Struts Response Utils를 활용 한 예
http://blog.naver.com/phrack?Redirect=Log&logNo=80053722954

JSTL을 활용한 예
http://blog.naver.com/phrack?Redirect=Log&logNo=80053722757

---------------------------------------------------------------------------­----------------------

Posted by 1010
01.JAVA/Java2011. 7. 11. 15:43
반응형

System.out.println("===========================================================================");
   String param = request.getParameter("viewComment");    // 테스트해보고 싶은 파라미터를 지정하세요.
   System.out.println("[TEST] 변환전: " + param);
   String charset[] = {"8859_1", "ascii", "UTF-8", "KSC5601", "EUC-KR", "MS949"};
   for(int i=0; i<charset.length ; i++){
       for(int j=0 ; j<charset.length ; j++){
           if(i==j){
               continue;
           } else{
            System.out.println("[TEST] "  + charset[i]+" : "+charset[j]+" :" +new String (param.getBytes(charset[i]),charset[j]));
           }
       } // inner for
   } // outer for
   System.out.println("===========================================================================");
Posted by 1010
01.JAVA/Java2011. 2. 22. 13:23
반응형

public static String getHanStr(String number) {

                StringBuffer sb = new StringBuffer();
                String[] numArr = { "", "일", "이", "삼", "사", "오", "육", "칠", "팔", "구" };

                // 16자리 어레이로 만들기
                try{
                        int len = number.length();
                        if (len > 16){ throw new Exception("자릿수가 초과했습니다"); }
                        int[] snum = new int[16];
                        for (int i = (snum.length - (len)); i < snum.length; i++){
                                int k = i - (snum.length - len);
                                String a = String.valueOf(number.charAt(k));
                                snum[i] = Integer.parseInt(a);
                        }
                        for (int j = 0; j < 4; j++){
                                int k = (j * 4);
                                if (snum[k] + snum[k + 1] + snum[k + 2] + snum[k + 3] > 0){
                                        if (snum[k] > 0){
                                                sb.append(numArr[snum[k]]).append("천");
                                        }
                                        if (snum[k + 1] > 0){
                                                sb.append(numArr[snum[k + 1]]).append("백");
                                        }
                                        if (snum[k + 2] > 0){
                                                sb.append(numArr[snum[k + 2]]).append("십");
                                        }
                                        if (snum[k + 3] > 0){
                                                sb.append(numArr[snum[k + 3]]);
                                        }
                                        switch (j) {
                                                case 0:
                                                        sb.append("조");
                                                        break;
                                                case 1:
                                                        sb.append("억");
                                                        break;
                                                case 2:
                                                        sb.append("만");
                                                        break;
                                                case 3:
                                                        sb.append("원");
                                                        break;
                                        }
                                }
                        }
                        return sb.toString();
                }catch(NumberFormatException e){
                        return "";
                }catch(Exception e){
                        return "";
                }
        }

Posted by 1010
01.JAVA/Java2011. 2. 12. 16:56
반응형
HttpRequestWithModifiableParameters param = new HttpRequestWithModifiableParameters(request); //요렇게 생성해서
param.setParameter(name, values); //요렇게 값 넣고

request = (HttpServletRequest)param; //req로 받으면 됩니다.




-------------


public class HttpRequestWithModifiableParameters extends HttpServletRequestWrapper {
 
    HashMap params;
    
    /**
     * @param request
     */
    public HttpRequestWithModifiableParameters(HttpServletRequest request) {
        super(request);
        this.params = new HashMap(request.getParameterMap());
    }
 
    /* (non-Javadoc)
     * @see javax.servlet.ServletRequest#getParameter(java.lang.String)
     */
    public String getParameter(String name) {
        String returnValue = null;
        String[] paramArray = getParameterValues(name);
        if (paramArray != null && paramArray.length > 0){
          returnValue = paramArray[0];   
        }
        return returnValue;
    }
    /* (non-Javadoc)
     * @see javax.servlet.ServletRequest#getParameterMap()
     */
    public Map getParameterMap() {
        return Collections.unmodifiableMap(params);
    }
    /* (non-Javadoc)
     * @see javax.servlet.ServletRequest#getParameterNames()
     */
    public Enumeration getParameterNames() {
        return Collections.enumeration(params.keySet());        
    }
    /* (non-Javadoc)
     * @see javax.servlet.ServletRequest#getParameterValues(java.lang.String)
     */
    public String[] getParameterValues(String name) {
    	String[] result = null;
    	String[] temp = (String[])params.get(name);
    	if (temp != null){
    		result = new String[temp.length];
    		System.arraycopy(temp, 0, result, 0, temp.length);    		
    	}
        return result;
    }
       
    
    /**
     * Sets the a single value for the parameter.  Overwrites any current values.
     * @param name Name of the parameter to set
     * @param value Value of the parameter.
     */
    public void setParameter(String name, String value){
      String[] oneParam = {value};
      setParameter(name, oneParam);
    }
    
    /**
     * Sets multiple values for a parameter.
     * Overwrites any current values.
     * @param name Name of the parameter to set
     * @param values String[] of values.
     */
    public void setParameter(String name, String[] values){
      params.put(name, values);   
    }
}
Posted by 1010
01.JAVA/Java2010. 12. 6. 17:19
반응형
 
낮에 파일에서 읽은 인코딩과 데이터베이스로 보내는 스트림의 인코딩이 달라서 고생을 좀 했습니다. 편법을 쓰면 금방 해결하는 일이었지만, 쩝... 오기가 생긴다거나 제대로 해야겠다는 것도 아니지만...^^; (어차피 모든 것을 다 알수는 없으니까요)
아무튼, 그동안 뒷전으로 미루었던 것들에 대해 처음부터 다시 한다는 기분으로 살펴보는 것도 좋겠다 싶더군요. 그래서 일단 코드를 짜봅니다.
먼저 파일 네 개를 인코딩이 전부 다르게 해서 만들었습니다. 데이터는 별 뜻 없이 넣었구요.
one, two, three and to the four
1234%^&*(||+)
거추장스러운 허식을 벗고 나면 무엇이 남을까?
난로는 그냥 쓰시고, 동아일보는 봄부터
영어, 숫자, 특수문자와 한글을 혼용해서 넣었는데, 생각해보니 ISO8859-1에 한글을 넣은 것은 좀 어처구니 없는 것 같기도 하지만, 일단 똑같이 데이터를 넣고 아래 코드를 실행해 봤습니다.
// 1. ms949, ISO-8859-1, UTF-8, UTF-16 포맷의 파일에서 읽은 문자열의 길이 비교

// 1.1 각 파일 데이터를 String으로 읽기
String files[] = {"ms949.txt", "iso8859-1.txt", "utf-8.txt", "utf-16.txt"};
String data[] = new String[4];

for(int i = 0; i < files.length; i++){
  BufferedReader reader = new BufferedReader(
    new FileReader(files[i]));
 
  data[i] = ""; // 초기화 하지 않으면, 반복문에서 null 이라는 글자가 추가됨
  String line;
  while((line = reader.readLine()) != null)
   data[i] += line + "/n";
 
  reader.close();
}

// 1.2 String 데이터 길이 비교
for(int i = 0; i < data.length; i++){
  System.out.println(files[i] + " " + data[i].length());
  System.out.println(data[i]);
}
일단 콘솔 출력은 다음과 같이 나옵니다. 유니코드는 엽기로 출력됩니다. 브라우저에서 많이들 보셨겠지만 ^^;
유심히 살펴볼 것은 String으로 읽은 값의 길이가 다르다는 점입니다.
ms949.txt 98
one, two, three and to the four/n1234%^&*(||+)/n거추장스러운 허식을 벗고 나면 무엇이 남을까?/n난로는 그냥 쓰시고, 동아일보는 봄부터/n
iso8859-1.txt 98
one, two, three and to the four/n1234%^&*(||+)/n?????? ??? ?? ?? ??? ????/n??? ?? ???, ????? ???/n
utf-8.txt 127
one, two, three and to the four/n1234%^&*(||+)/n嫄곗텛?옣?뒪?윭?슫 ?뿀?떇?쓣 踰쀪퀬 ?굹硫? 臾댁뾿?씠 ?궓?쓣源??/n?궃濡쒕뒗 洹몃깷 ?벐?떆怨?, ?룞?븘?씪蹂대뒗 遊꾨???꽣/n
utf-16.txt 178
??
인코딩 String 길이 파일 크기(bytes)
MS949 98 131
ISO-8859-1 98 96
UTF-8 127 166
UTF-16 178 194

글쎄요.. 인코딩에 대해서 본격적으로 다뤄보기 전에 일단 문제의 발단부터 해결까지의 과정을 먼저 말씀드려야겠네요. 우선, 4메가(4351960) 정도 되는 우편번호 데이터를 넣는 sql 파일이 있습니다. MS949로 인코딩된 sql 파일을 그대로 실행시켜서, 데이터베이스에 넣었더니, 자바 클래스에서 비교를 하니까 다 깨지더군요. 그래서 아예 파일 자체를 UTF-8로 갖고 있으면 편하겠다 싶었죠. ^^;
그래서, MS949 파일의 데이터(insert 문장을 길게 나열한 것이죠.)를 BufferedReader.readLine()올 한 줄씩 읽어서 DataOutputStream.writeUTF() 로 쐈더니만 모든 행의 첫 줄에 입력하지 않는 글자들이 하나씩 붙더군요. ㅡㅡ;  API를 다시 보니, 일반, UTF-8이 아니라 modified UTF-8를 썼습니다. 그냥 간단히 살펴보면
'u0001'과 'u007F' 사이의 문자는 다음과 같이 한 바이트로 나타내고,
Bit Values
Byte 1
0
bits 6-0
널 문자인 'u0000'와 'u0080'과 'u07FF' 사이의 문자는 두 바이트로
Bit Values
Byte 1
1
1
0
bits 10-6
Byte 2
1
0
bits 5-0
'u0800'과 'uFFFF' 사이의 문자는 세 바이트로 나타낸다고 합니다.
Bit Values
Byte 1
1
1
1
0
bits 15-12
Byte 2
1
0
bits 11-6
Byte 3
1
0
bits 5-0
여하튼, UTF-8의 결함을 보완하기 위해서 수정한 것이라지만, 앞에 붙는 문자들로 인해서 ANT의 sql 태스크가 실행되지 않았습니다. DBMS에서 SQL 실행이 안되는 것이죠. insert 앞에 이상한 것들이 붙어 있으니까.. 문법 오류가 나죠. ^^;
문제를 어떻게 해결하느냐?
먼저 떠오르는 것은 이클립스를 이용한 꽁수였습니다. MS949 파일을 열어서 CTRL+A 하고 나서 CTRL+C 하고, 다시 UTF-8 포맷의 파일을 열어서 CTRL+V 하는 방법이죠. ^^;
두번째는 일일이 바이트를 인코딩해주는 것입니다. 호기심 좀 채우자고, RFC 같은 것을 읽을 수는 없으니까(너무 길더군요) URLEncoder의 encode() 메소드 소스를 보면 할 수 있을 것 같더군요. 뭐, 시간이 되면... 톰캣에 있는 encode() 메소드 구현이나 java.net.URLEncoder의 구현을 보고, 글을 올리겠습니다. 장담은 못하구요. ^^;
마무리는 꽁수가 아니라, JDK 5.0으로 해결한 이야기를 해드리죠.

꽁수를 써서 처음엔 해결하려고 해도 자꾸 이클립스에서 Heap이 넘쳐나는 에러가 나더군요. ^^;
그래서 다른 방법을 찾았는데 나중에 이 문제는 다시 부딪히게 되었습니다. 그건 나중에 이야기하구요. JDK5.0에서는 PrintWriter 클래스의 생성자 중에서 File과 인코딩 방식을 인자로 넣어주는 것이 있더군요. 그것을 사용하니까 잘 되었습니다. 너무 쉽게...^^; (테스트 코드를 첨부합니다.)
public void testReadByUTF8() throws Exception{
      
       BufferedReader reader = new BufferedReader(
               new FileReader("ref/zipcode_mysql6.sql"));
      
       PrintWriter writer = new PrintWriter(
               new File("setup/zipcode-mysql-6-data.sql"), "UTF-8");

      
       String line;
       while ((line = reader.readLine()) != null) {
           System.out.println(line);
           writer.println(line);
       }      
      
       reader.close();
       writer.close();
  }
프로그램이 잘 실행되었습니다. sysout(System.out.print) 한 내용도 문제 없이 출력되었구요. 파일에 데이터도 잘 복사되었습니다. Ant에서 sql 태스크를 수행하는데 잘 되다가 돌연 에러가 났습니다. 에러가 난 지점을 확인해보니.. 출력이 되다가 말았더군요. ㅡㅡ;
하필, 47,706건 중에서 100개도 안남긴 충북 청원군 부용면 부강8리의 데이터 삽입 구문이 출력되다 말았습니다. 정확히, 4,874,058  bytes 에서 문제가 발생한거죠. TaskMgr(작업 관리자)의 메모리 확인해보고, VM 옵션으로 힙(Heap) 크기를 크게 줘봐도 변화가 없었습니다. ㅡㅡ;
에러가 나는데, .log는 도대체 어디 있는건지. 탐색기에서 검색으로 찾아봤더니 이클립스의 로그 파일인 .log는 프로젝트 디렉토리 아래의 .metadata 디렉토리에 있었습니다.
java.lang.OutOfMemoryError: Java heap space
이걸로 어쩌란 것인지..ㅡㅡ;
결국은 이클립스 뉴스리스트를 보고 알았습니다. 이클립스에서 프로그램을 실행할 때 VM 옵션을 수정해줘봐야 소용이 없고, eclipse를 실행할 때 VM 옵션을 수정해야 합니다. 이클립스가 실행될 때의 디폴트 힙 사이즈는 128MB인 모양입니다.(정확하지는 않습니다. ^^;)
그런데, 플러긴까지 설치하면 금방 넘어가겠죠. 그랬던 것입니다. 고작 5MB도 안되는 파일을 읽다가 멈췄다기 보다, 딱 4MB 정도 남았는데 제가 파일을 로딩하면서 힙 사이즈를 초과한 것이라고 추측이 됩니다. 처음에 말했던 꽁수를 부릴 때도 에러가 난 것도 마찬가지 이유죠.
eclipse.exe -vm <자바 설치 디렉토리>jrebinjavaw.exe -vmargs -Xmx512M
위와 같이 실행했더니 무사히 앞서 했던 작업을 마칠 수 있었습니다. 이클립스 역시 VM 상에서 돌아간다는 것을 깜빡했던거죠. 이클립스가 수행되는 VM의 힙 사이즈를 늘려서 문제는 해결되었습니다.
저처럼 평소에 더블클릭으로만 이클립스를 띄웠던 분들은 참조하세요.
eclipse [platform options] [-vmargs [Java VM arguments]]
-vmargs args VM에 옵션을 전달하고자 할 때

닫기
퍼온 글

출처: http://blog.naver.com/inking007/120001550360
문자 코드 변환 과정

1. 컴파일
컴파일시 원시파일의 문자열을 현재 환경(한글환경 KSC5601)의 locale로 인코딩하여 읽어 들인후
유니코드에서의 대응하는 코드값으로 변환한다.
원시파일에서  '한글' 이라는 문자열은 다음과 같은 hex code를 가지고 있다. (KSC5601)
c7 d1 b1 db
2.실제 .class 파일로 저장시 UTF-8로 변환하여 저장한다.
예를 들어 다음과 같은 코드 작성후
컴파일하면
public class Test {

   public static void main(String args[]) {
        String str = "한글";
       System.out.println(str);
  }

}
클래스파일 Test.class에 '한글' 이라는 문자열은 다음과 같이 인코딩되어 저장된다.
ED 95 9C EA B8 80
디컴파일 하면 다음과 같이 변환된다. UTF-8 --> UTF-16
        String s = "uD55CuAE00";
uD55C  '한'
uAE00  '글'
아래 URL을 클릭하면 해당 Unicode가 나타내는 문자및 UTF-8코드를 볼수 있다.
3. 실행시 문자열을 UTF-16으로 변환하여 로드하고 화면에 출력시 디폴트 인코딩으로 출력한다.





닫기
인코딩에 따라 글자수 인식이 달라진다.
인코딩이 되지 않고 한글이 입력될 때는(ISO8859-1로 추정)
'이호'라고 입력하면 6자로 인식된다.
한글 한 글자를 3자로 인식하는 것이다.
'육봉달'이라고 입력하면 9.. 받침하고 상관없다.
'튕뽃뾳'이라고 해도.. 9
이번엔 인코딩을 해본다.
먼저 UTF-8
위의 세 개의 문자를 대상으로 테스트하면
2, 3, 3 글자.. 역시 받침에 상관없고
한글 한 글자를 영문 알파벳 하나와 동일하게 인식한다.
퉧뾳뾳, 111, aaa, %%%
위의 입력 모두 동일하게 3글자로 인식한다.
EUC-KR은 다를까?
위의 네개 문자 조합을 입력하면 나머지 셋은 3 글자로 인식하지만
한글인 '퉧뾳뾳'은 2글자로 인식하여 모두 6글자가 된다.
이것만 보고 EUC-KR은 한글을 두 자로 인식하구나 일반화하는 것은 위험한 일이었다.
다음과 같은 기이한 현상을 보인다.
퉧뾳뾳 : 제대로 인코딩을 못하며 24글자로 인식한다.(&#53863;&#49075;&#49075;)
이쁋: 역시 두 번째 글자는 인식을 제대로 못해 9자가된다.(이&#49227;)
제대로 인식하지 못하는 글자에 대해서는 8글자가 배당됨을 알 수 있다.
EUC-KR을 관행처럼 쓰고 있는 가운데
UTF-8을 쓰자할 때 뭐가 장점이냐 물으면.. 사실 떠오르는 것이 별로 없었는데
한가지 예로 들 수 있는 현상이다.
UTF-8에선.. 뾳뛕쁋쿅푝 같은 외계어같은 문자마저 올바로 5글자로 인식한다는 면에서 EUC-KR보다 실용성이 높다고 할 수는 없지만 신뢰성이 높은 것은 분명한 것이다.


출처 : http://blog.iampro.net/2368
Posted by 1010