2008/10/05 17:50

구글에 지리공간정보를 제공하는 방법(Opening the door to geospatial data)

구글 LatLong 블로그 10월 2일자 소식입니다. 한마디로, 여러가지 지리공간정보(Geospatial data)를 구글에 제공하고자 할 때 어떻게 해야 하는지를 알려주는 글입니다.

구글어스(Google Earth)나 구글맵(Google Maps)에 사용되는 정보는 거의 대부분 외부에서 제작된 것입니다. 구글에서 직접 생산되는 것은 거의 없다고 생각해도 무방합니다.

위성영상의 경우 예전에는 Digital Globe사의 60cm급 QuickBird 영상을 독점적으로 사용하다가, 최근 GeoEye-1이 발사되면서 GeoEye사와 구글이 인터넷에 관한한 독점계약을 맺었습니다. 도로지도의 경우에는 예전에는 텔레아틀라스(TeleAtlas)와 나브텍(Navteq) 자료를 동시에 사용하다가 최근에 텔레아틀라스 도로지도만 사용하기로 독점계약을 맺었죠.(여기 참조)

항공사진은 대부분 지방자치단체에서 촬영하기 때문에 구글에서 각각의 지방자치단체와 사용권계약을 맺어서 사용하고, 3차원 빌딩 자료의 경우, 지방자치단체와 계약을 맺는 경우도 있고, Sanborn과 같은 전문회사에서 제작한 자료를 사용하는 경우도 있지만, 대부분 일반 사용자들이 3D 이미지갤러리에 올린 자료중에서 품질이 좋은 것을 선별하여 올리고 있고요.

구글어스에 어떠한 자료가 사용되는지에 대해 자세히 알고 싶으시면 영상부분, 지형/3차원모델부분 , 도로지도 및 기타 부분을 확인해 보시기 바랍니다.

구글어스(Google Earth)의 지구 모습

하지만, 저도 이제까지 구글에서 어떠한 방식으로 3차원 빌딩이나 항공사진 등이 제작된 지방자치단체와 접촉하는지에 대해 아는바가 없었습니다. 공개적으로 요청한 적이 있었는지나 잘 모르겠네요. 하여튼 이 글에서는 자료를 제공 혹은 공유하고자 하는 사람들이 어디로 연락하면 되는지를 공식적으로 발표했네요.

구글에서 어떤 정보를 필요로 하는지, 어떤 포맷을 원하는지는 여기를 보시면 됩니다. 이 이외에도 항공사진(정사사진)도 원하고 있고요.
  • 텍스처가 있는 3D 빌딩(.shp, .kmz, .skp, .dae, .3ds, .max 등 포맷)
  • 텍스처가 없는 3D 빌딩(.shp, .kmz, .skp, .dae, .3ds, .max 등 포맷)
  • 높이만 있는 빌딩
  • 래스터(영상) 형태의 지형 혹은 고도 자료
자료를 갖고 있고, 이를 공유할 의향이 있는 기관은 연락용 폼(contact form)이나, gisdata@google.com 로 연락하시면 됩니다.

머... 우리나라에서는 (다른 나라도 마찬가지겠지만) 대부분의 지리공간정보를 국가나 지방자치단체에서 생산하고 있습니다. 국민의 세금으로 만들어진 자료를 우리나라도 아닌 외국기업에게 제공하는 일은 쉽지 않을 것 같습니다.

참고로, 아래에 들어 있는 비디오를 보시면 구글에 지리공간정보를 제공하면 어떠한 잇점이 있는지를 설명하고 있습니다.

")//]]>

민, 푸른하늘

====
http://google-latlong.blogspot.com/2008/10/opening-door-to-geospatial-data.html
Thursday, October 2, 2008 at 8:30 AM
Posted by JL Needham, Manager of Public Sector Content Partnerships
[This post recently appeared on the new Content Central blog; it's a great look where some of our geospatial data comes from and how organizations can share data with us. -Ed.]

여러분의 기관이 보유한 항공사진이나 관심장소와 같은 지리공간자료(geospatial data)를 어떻게 구글맵(Google Maps)나 구글어스(Google Earth)에 추가할 수 있는지 고민해보신 적이 있으십니까? 아마도 구글맵 API를 사용하고 있으시면서 여러분의 자료를 사용하여 우리 기본도를 수정하고 싶으실지도 모르겠습니다. 혹은 여러분의 커뮤니티가 자주 마주치는 곳에 데이터를 보여줌으로써, GIS(공간정보시스템, 지리정보시스템)에 대한 여러분의 기관의 투자범위를 확대하기만 원할 수도 있겠죠.

구글 공공부분 콘텐츠 파트너십 팀원 몇몇이 최근
ESRI 국제 유저컨퍼런스(International User Conference)에 참석하여, 매일 GIS 자료와 먹고 자고 숨쉬는 수많은 지방정부 GIS 관리자 분들을 만났습니다. 그 자리에서 우리는 "구글과 자료를 공유하고 싶은데, 어떻게, 누구와 접촉해야 할지 모르겠다"는 말을 수없이 들었습니다.

솔직히, 우리는 여러분의 GIS 데이터가 구글 서비스로 들어오는지에 대해 잘 설명드리지 못했던 점 인정합니다. 우리 구글에서 어떤 종류의 자료를 원하는지, 어떤 포맷 혹은 품질 수준은 어떤지 등등에 대해 말입니다. 그러나, 우리는 나름대로 노력하고 있으며, 다음과 같은 2가지 예를 참고하시기 바랍니다.

올해초 우리는 지방정부나 커뮤니티 그룹, 교육기관 등에서 자신의 동네의 3차원 모델을 구글어스에 올릴 수 있도록 도와드리는 3차원 도시 프로그램을 도입하였습니다.  "3차원 도시" 비디오 투어
와 성공적으로 3D 모델링을 성공한 프로젝트를 담은 성공사례(case studies)를 살펴 보시면, 3차원 모델을 구축하고 공유함으로써, 도시계획이나 경제 발전, 관광산업 혹은 단순한 구경거리로서 활용하는 사례를 보실 수 있습니다. 이 사이트는 최근 다양한 유럽언어를 지원하게 되었으며, 3D 모델링 프로젝트를 수행하는 절차를 설명하고, 구글에서 환영하는 자료의 종류 및 포맷이 설명되어 있습니다. 물론, 3차원 도시프로그램 사이트에는 공유하고자하는 3D 데이터(항공사진 및 지형자료 포함)에 대해 의견을 나눌 수 있는 연락용 폼(contact form)이 있습니다.

")//]]>

GIS 데이터를 구글 사용자에게 제공할 수 있도록 열어둔 문의 또다른 예는 선거 정보 프로젝트입니다. 구글은 이 운동의 파트너로서, 미국의 유권자들이 선거 등록, 투표소의 위치, 투표소에 갔을 때 알아야 할 것 등에 대한 정보를 온라인으로 찾아볼 수 있도록 하자는 것입니다. 선거 관련 공무원들께서 더 많이 참여해 주심으로써, 우리는 투표소를 구글맵에서 확인할 수 있게 되었으며, 기타 많은 관련정보를 제공할 수 있게 되었습니다.

구글맵 투표소 위치 정보

이처럼, 문은 열려 있습니다. 여러분의 기관이 3차원 데이터를 공유하고 싶으시거나, 3차원 모델을 생성하는 방법에 관심이 있으시다면 알려주시기 바랍니다. 여러분의 기관에서 투표소의 위치를 포함한 공식 선거정보를 제공하여 주시거나 관내의 유권자들에게 좀더 편하게 이러한 정보를 구글맵에 제공하기를 바라신다면, 선거정보 프로젝트에 들어 있는 선거정보 공유용 공개 포맷을 확인해 보시고, 원하는 바를 VIP 팀에게 알려주세요.

또한 기타 다른 GIS 데이터를 공유하고 싶으시다면, gisdata@google.com에 알려주시거나, 콘텐트 센트럴 블로그(Content Central blog)에 귀기울여주시기 바랍니다. 여러분의 GIS 데이터를 구글 서비스에 제공할수 있는 또다른 정보를 제공할 예정이니까요.

====
Trackback 0 Comment 0
2008/09/08 15:07

Log4j 사용법 로깅-Log4J

Log4j 사용법 <?xml:namespace prefix = o ns = "urn:schemas-microsoft-com:office:office" />

 

Log4j 상세 설명 및 예제 프로그램

 

 

 

 

 

 

 

작성자:          <?xml:namespace prefix = st1 ns = "urn:schemas-microsoft-com:office:smarttags" />홍혜성 hicomet@nate.com

작성일:          200613

 

Log4j 사용 예제 프로그램(1)

[1] Log4jTest.java

 

import org.apache.log4j.Logger;

              

public class Log4jTest

{

        protected static final Logger logger = Logger.getLogger(Log4jTest.class);

              

        public static void main(String[] args)

        {

               if (logger.isDebugEnabled()) {

                       logger.debug("프로그램 DEBUG");

               }

               if (logger.isInfoEnabled()) {

                       logger.info("프로그램 INFO");

               }

                logger.warn("프로그램 WARN");

               logger.error("프로그램 ERROR");

               logger.fatal("프로그램 FATAL");

        }

}      

 

[2] log4j.properties

 

## info 이상만 출력 허용 (debug Priority은 출력되지 않음)

## ROOT appender R 로 지정함

log4j.rootCategory=info, R

              

## 콘솔에 출력

log4j.appender.R=org.apache.log4j.ConsoleAppender

              

## 패턴 : 날짜출력 Priority출력 클래스명출력 - 메시지출력 개행

log4j.appender.R.layout=org.apache.log4j.PatternLayout

log4j.appender.R.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss,SSS} %-5p %c{2} %x - %m%n

 

[3] 결과

 

D:\study\log4j>javac Log4jTest.java

D:\study\log4j>java Log4jTest

2006-01-02 16:19:18,671 INFO  Log4jTest  - 프로그램 INFO

2006-01-02 16:19:18,671 WARN  Log4jTest  - 프로그램 WARN

2006-01-02 16:19:18,671 ERROR Log4jTest  - 프로그램 ERROR

2006-01-02 16:19:18,671 FATAL Log4jTest  - 프로그램 FATAL

 

Log4j 사용 예제 프로그램 (2)

 [1] Log4jTest.java

 

import org.apache.log4j.Logger;

import org.apache.log4j.PropertyConfigurator;

              

public class Log4jTest

{

        public static void main(String[] args)

        {

               // ROOT

               Logger rootLogger = Logger.getRootLogger();

               rootLogger.info("프로그램 시작");

              

               // Logger1

               Logger logger1 = Logger.getLogger("com.search1");

               if (logger1.isDebugEnabled()) {

                       logger1.debug("프로그램 DEBUG");

               }

               if (logger1.isInfoEnabled()) {

                       logger1.info("프로그램 INFO");

               }

               logger1.warn("프로그램 WARN");

               logger1.error("프로그램 ERROR");

               logger1.fatal("프로그램 FATAL");

                             

               // Logger2

               Logger Logger2 = Logger.getLogger("com.search2");

               if (Logger2.isDebugEnabled()) {

                       Logger2.debug("프로그램 DEBUG");

               }

               if (Logger2.isInfoEnabled()) {

                       Logger2.info("프로그램 INFO");

               }

               Logger2.warn("프로그램 WARN");

               Logger2.error("프로그램 ERROR");

               Logger2.fatal("프로그램 FATAL");

              

               // ROOT

               rootLogger.info("프로그램 끝");      

        }

}

 

[2] log4j.properties

 

## info 이상만 출력 허용 (debug Priority은 출력되지 않음)

## ROOT appender R 로 지정함

log4j.rootCategory=info, R

 

## com.search1 패키지의 로깅에는 warn 이상만 출력

## additivity = false 로 지정했으므로 log4j.search1.log 에만 출력됨

## additivity = true 로 지정하면 log4j.log, log4j.search1.log 모두에 출력됨

log4j.category.com.search1=warn, search1

log4j.additivity.com.search1=false

 

## com.search2 패키지의 로깅에는 info 이상만 출력

log4j.category.com.search2=info, search2

log4j.additivity.com.search2=false

 

## DailyRollingFileAppender 는 파일 교체주기를 월, , , 하루 2, 시간, 분 별로 정할수 있는 Appender

log4j.appender.R=org.apache.log4j.DailyRollingFileAppender

 

## priority 설정, 만약 Threshold=fatal 을 설정하면

## log4j.category.com.search1=warn, search1 를 설정했더라고 fatal 이상만 출력 된다.

log4j.appender.R.Threshold=debug

 

## ImmediateFlush 기본값은 true. 로그메세지들이 전혀 버퍼되지 않는 것을 의미

log4j.appender.R.ImmediateFlush=true

 

## log4j.log 파일에 출력 (디렉토리는 미리생성해 놓아야 에러 나지 않음)

log4j.appender.R.File=/study/log4j/log/log4j.log

 

## 기본값은 true이며 파일 끝에 추가하는 것을 의미한다.

## false 는 각각의 프로그램이 시작할때 파일에 덮어씌운다.

log4j.appender.R.Append=true

 

## 교체주기를 월, , , 하루 2, 시간, 분 별로 정할수 있다.

## 형식은 SimpleDateFormat 을 따른다. (":" 문자는 사용 금지)

log4j.appender.R.DatePattern='.'yyyyMMdd

 

## 패턴 : 날짜출력 Priority출력 클래스명출력 - 메시지출력 개행

log4j.appender.R.layout=org.apache.log4j.PatternLayout

log4j.appender.R.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss,SSS} %-5p %c{2} %x - %m%n

 

log4j.appender.search1=org.apache.log4j.DailyRollingFileAppender

log4j.appender.search1.Threshold=debug

log4j.appender.search1.ImmediateFlush=true

log4j.appender.search1.File=/study/log4j/log/log4j.search1.log

log4j.appender.search1.Append=true

log4j.appender.search1.DatePattern='.'yyyyMMdd

log4j.appender.search1.layout=org.apache.log4j.PatternLayout

log4j.appender.search1.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss,SSS} %m%n

              

log4j.appender.search2=org.apache.log4j.DailyRollingFileAppender

log4j.appender.search2.Threshold=debug

log4j.appender.search2.ImmediateFlush=true

log4j.appender.search2.File=/study/log4j/log/log4j.search2.log

log4j.appender.search2.Append=true

log4j.appender.search2.DatePattern='.'yyyyMMdd

log4j.appender.search2.layout=org.apache.log4j.PatternLayout

log4j.appender.search2.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss,SSS} %m%n

 

[3] 결과

 

(1) log4j.log

        2006-01-02 16:29:35,984 INFO  root  - 프로그램 시작

        2006-01-02 16:29:35,984 INFO  root  - 프로그램 끝

 

(2) log4j.search1.log

        2006-01-02 16:29:35,984 프로그램 WARN

        2006-01-02 16:29:35,984 프로그램 ERROR

        2006-01-02 16:29:35,984 프로그램 FATAL

 

(3) log4j.search2.log

        2006-01-02 16:29:35,984 프로그램 INFO

        2006-01-02 16:29:35,984 프로그램 WARN

        2006-01-02 16:29:35,984 프로그램 ERROR

        2006-01-02 16:29:35,984 프로그램 FATAL

Log4j 상세 설명

org.apache.log4j.Logger 사용

 

n         Category extends 받은 Logger 클래스 사용

ü        org.apache.log4j.Logger extends org.apache.log4j.Category)

ü        Category.getInstance() 방식은 deprecated .
대신 Logger.getLogger() 방식으로 대체됨

 

      public static Logger getLogger(String name);

      public static Logger getLogger(Class clazz);

      public static Logger getRootLogger();

      public static Logger getLogger(String name, LoggerFactory factory);

 

주요 컴포넌트

 

n         Categories

n         appenders

ü        콘솔, 텍스트파일, html 파일, xml 파일, 소켓, Windows NT Event Log, 이메일 전송 가능

ü        ConsoleAppender, FileAppender, SMTPAppender, SocketAppender, NTEventLogAppender, SyslogAppender, JMSAppender, AsyncAppender, NullAppender

n         layouts : 메시지 형식

 

Priority(우선권)

 

      debug : 디버깅 메세지

      info : verbose 모드에서 출력될만한 메세지

      warn : 이상없이 계속 실행될 수 있는 정도의 경고메세지

      error : 그럭저럭 돌아갈만한 정도의 에러메세지

      fatal : 비정상적으로 종료될 치명적인 메시지

 

Appender 옵션

 

전체 옵션

log4j.configuration=app_config.properties

log4j.debug=true

log4j.disable=INFO

log4j.additivity.your.category.name=false

log4j.defaultInitOverride=false

log4j.disableOverride=false

ConsoleAppender

Threadhold=WARN

ImmediateFlush=true

Target=System.err

FileAppender

Threadhold=WARN

ImmediateFlush=true

File=mylog.txt

Append=false

RollingFileAppender

Threadhold=WARN

ImmediateFlush=true

File=mylog.txt

Append=false

MaxFileSize=100KB

MaxBackupIndex=2

DailyRollingFileAppender

Threadhold=WARN

ImmediateFlush=true

File=mylog.txt

Append=false

DatePattern='.'yyyy-ww

 

n         log4j.configuration=app_config.properties

ü        ex> java –Dlog4j.configuration= app_config.properties … 형식으로 속성 파일 설정

n         log4j.debug=true

ü        기본값은 false. log4j 를 설정하는 상세 정보를 출력

n         Log4j.disable=INFO

ü        모든 category에서 여기 지정한 priority 보다 같거나 낮은 priority 메세지는 로깅하지 않는다. (log4j.disableOverride=false 일때에만 동작)

n         log4j.additivity.your.category.name=false

ü        기본값은 true. appender ancestor(조상)으로부터 쌓이게 할지(true) 그렇게 하지 않을지(false)를 지정한다.

n         log4j.defaultInitOverride=false

ü        Category.getRoot() Category.getInstance(...) 메소드를 처음으로 호출하여   Log4j 의 초기화 과정을 마친다.("log4j.debug=true" 로 지정하여 초기화가 일어나는 것을 볼 수 있다) 초기화되는 동안 Log4j 는 어플리케이션의 클래스패스에서 "log4j.properties" 파일이나 "log4j.configuration=app_config.properties" 프로퍼티를 통해 지정한 파일을 찾는다. 만약 이것(프로퍼티를 통해 지정한 환경파일을 찾는 것)을 원하지 않는다면 이 프로퍼티를 true 로 세팅하라.

ü        이것을 시스템 property로 설정할 필요가 있다. 예를 들어 다음과 같이 프로그램을 구동한다. java -Dlog4j.defaultInitOverride=true ...   왜냐하면 설정파일에 세팅했다면 이미 너무 늦기 때문이다.Log4j는 이미 그 파일을 읽기위해 시작되었을 것이다.

n         log4j.disableOverride=false

ü        기본값은 false. 가끔 true로 설정하여 log.disable 프로퍼티를 무시할 수 있다.

n         Threadhold=WARN

ü        appender category priority가 더 낮게 지정되어 있다고 할지라도 여기 명시된 priority보다 낮은 메세지들을 로깅하지 않을 것이다. 이것은 콘솔에 모든 메세지가 나타나는 동안 파일에 로깅되는 경우와 같이 메세지의 숫자를 줄이는데 유용하다.

n         ImmediateFlush=true

ü        기본값은 true. 로그메세지들이 전혀 버퍼되지 않는 것을 의미하며 대부분의 상황에 적당하다.

n         Target=System.err

ü        기본값은 System.out

n         File=mylog.txt

ü        로깅할 파일명. 앞부분에 경로를 나타내기 위해 ${some_property_key} (예를 들어 java.home 또는 user.home 과 같은 시스템 프로퍼티)를 사용할 수 있다. 사실 모든 옵션들의 프로퍼티키들은 이런 종류의 값을 설정 가능하다.

n         Append=false

ü        기본값은 true이며 파일 끝에 추가하는 것을 의미한다. false 는 각각의 프로그램이 시작할때 파일에 덮어씌운다.

n         MaxFileSize=100KB

ü        끝에 KB, MB 또는 GB를 붙인다. 지정한 크기에 도달하면 로그파일을 교체한다(roll).

n         MaxBackupIndex=2

ü        최대 2()의 백업 파일들을 유지시킨다. 오래된 파일들은 삭제한다. 0 은 백업파일을 만들지 않는다.

n         DatePattern='.'yyyy-ww

ü        매주마다 파일을 교체(roll)한다. 교체주기를 월, , , 하루 2, 시간, 분 별로 정할수 있다. 이 값은 교체주기를 설정할 뿐만 아니라 백업파일의 붙는 문자열도 정한다.

ü        콜론(:) 문자를 값의 어디에도 사용하지 말라. 그것 말고는 자바의 SimpleDateFormat 의 어떤 형식 문자열을 사용할 수 있다. 특히 한쌍의 작은따옴표(single quote) 안에 있는 문자를 반드시 제어해주어야 한다.(예의 '.' 와 같이)

ü        '.'yyyy-MM: 매달의 첫날에 로그파일을 교체한다.

ü        '.'yyyy-ww: 매주의 첫날에 로그파일을 교체한다.

ü        '.'yyyy-MM-dd: 매일 자정에 로그파일을 교체한다.

ü        '.'yyyy-MM-dd-a: 매일 자정정오에 로그파일을 교체한다.

ü        '.'yyyy-MM-dd-HH: 시간마다(시간이 시작할때) 로그파일을 교체한다.

ü        '.'yyyy-MM-dd-HH-mm: 분마다(분이 시작할때) 로그파일을 교체한다.

 

layout 옵션

 

PatternLayout

ConversionPattern=%m%n

HTMLLayout

LocatoinInfo=true

Title=My app title

XMLLayout

LocatoinInfo=true

TTCCLayout

(이것 대신 더 유연한 PatternLayout을 사용하라)

DateFormat=ISO8601

TimeZoneID=GMT-8:00

CategoryPrefixing=false

ContextPrinting=false

ThreadPrinting=false

 

n         ConversionPattern

ü        %d{yyyy-MM-dd HH:mm:ss,SSS} : 날짜 출력 {} 내용은 SimpleDateFormat 형식을 따름

ü        %-5p : Priority 출력 (fatal, error, warn, info, debug)

ü        %n : 개행 문자 출력

ü        %t : 자바 스레드명 출력

ü        %F : 파일명출력

ü        %L : 라인넘버 출력

ü        %c{2} : 클래스명 출력.

예를 들어 category 이름이 "a.b.c" 일때 %c{2} "b.c"를 출력하며 {2}는 도트(.)로 구분된 category 이름의 마지막 두개의 컴포넌트를 의미한다. {n} 이 없으면 기본적으로 카테고리의 이름을 모두 출력한다.

ü        %x : 로깅이벤트를 발생시킨 스레드에 관련된 내포검사항목(Nested Diagnostic Context : NDC)을 출력한다.

ü        Java Servlet 과 같이 다수의 클라이언트가 분산된 스레드에 의해 다루어 질 때 유용하다.

ü        %m : 메시지 출력

ü        %r : 어플리케이션이 시작되어 로깅이벤트가 일어날때까지의 경과시간을 밀리세컨드 값으로 출력

ü        %% : 하나의 % 기호를 출력

ü        %l : 소스코드의 위치정보를 출력. %C. %M(%F:%L) 의 축약형

ü        %C : 호출자의 완전한 클래스이름을 출력. %C. %M(%F:%L) 의 축약형

ü        %M : 로깅요청을 일으킨 메소드를 출력

 

ü        필드 안에서 완쪽 정렬을 하기위해 마이너스 기호(-)를 사용

ü        %20c : 최소 20문자 공간에서 category 이름을 (기본적인)오른쪽 정렬

ü        %-20c : 최소 20문자 공간에서 category 이름을 왼쪽 정렬

ü        %.30c : 만약 category 이름이 30문자보다 길면 (앞에서부터)자른다. 최소폭이 없으므로 30문자보다 짧다고 해도 여백공간은 없다.

ü        %20.30c : category 이름이 20문자보다 짧으면 오른쪽 정렬을 하고, 30자보다 길면 시작부터 자른다.

ü        %-20.30c : category 이름이 20자보다 짧으면 왼쪽 정렬을 하고, 30자보다 길면 시작에서부터 자른다.

 

è [경고] 프로그램의 실행속도를 느리게 하는 옵션 : %d, %l, %C, %M, %F, %L

n         LocatoinInfo=true

ü        기본값은 false. 자바파일명과 행번호를 출력한다.

n         Title=My app title

ü        기본값은 Log4j Log Message. HTML <title>태그에 주어진다

n         DateFormat=ISO8601

ü        자바의 SimpleDateFormat 의 생성자이거나 NULL, RELATIVE, ABSOLUTE, DATE, ISO8601 중 하나.

n         TimeZoneID=GMT-8:00

ü        TimeZone.getTimeZone(java.lang.String) 메소드에 의해 나올만한 형식의 문자열

n         CategoryPrefixing=false

ü        기본값은 true. category 이름을 출력한다.

n         ContextPrinting=false

ü        기본값은 true. 현재 스레드에 속하는 내포검사항목 정보를 출력한다.

n         ThreadPrinting=false

ü        기본값은 true. 스레드명을 출력한다.

 

Log4j 참고 사항

성능

 

n         코드상에서 성능이 매우 중요한 곳에서는 로깅메소드의 파라미터를 만드는 시간을 절약하기 위해 다음과 같이 사용한다. (isDebugEnabled(), isInfoEnabled() 사용)

 

if (Logger.isDebugEnabled() {

    Logger.debug("debug code");

}

 

n         SimpleLayout을 사용하였거나 %p, %m, %n의 패턴만 사용하여 설정한 경우 System.out.println(...) 만큼 성능이 빠르다고 테스트됐다.

n         ConversionPattern 에서 프로그램의 실행속도를 느리게 하는 옵션 è %d, %l, %C, %M, %F, %L

 

내포검사항목(NDC) 사용

 

n         가끔 서블릿처럼 하나의 코드가 각각의 클라이언트를 서비스 하기위해 여러개의 스레드로 인스턴스화 되어 많은 클라이언트를 서비스한다. 이때 다른 클라이언트로 부터의 로깅 요청을 차별화하기 위해서 내포검사항목(NDC)를 사용할 수 있다. 할 수 있는 것은 로깅하기 전에 클라이언트의 고유한 정보를 NDC로 넣는다. 고유한 정보는 클라이언트의 IP주소, 호스트이름 또는 요청과 관련된 어떤 것도 될 수 있다. %x layout 패턴에 명시하면 요청한 로그 메세지에 그 문자열(고유한 정보)을 찍는다.

 

[NDC 사용 프로그램 예제 (Log4jTest.java)]

import org.apache.log4j.Logger;

import org.apache.log4j.NDC;

 

public class Log4jTest

{

        protected static final Logger logger = Logger.getLogger(Log4jTest.class);

 

        public static void main(String[] args)

        {

               //BasicConfigurator.configure();

              

               NDC.push("Client #45890");

 

               // ROOT

               Logger logger = Logger.getRootLogger();

               logger.info("프로그램 시작");

 

               switchNDC();

              

               // ROOT

               logger.info("프로그램 끝");  

        }

       

        static void switchNDC() {

               NDC.push("Client #99999");

               logger.info("프로그램 중간");

               NDC.pop();

        }

}

 

[NDC 사용 프로그램 예제 결과]

2006-01-02 18:02:11,984 INFO  root Client #45890 - 프로그램 시작

2006-01-02 18:02:11,984 INFO  Log4jTest Client #45890 Client #99999 - 프로그램 중간

2006-01-02 18:02:11,984 INFO  root Client #45890 - 프로그램 끝

 

기타 참고 사항

 

n         root category는 항상 지정된 priority가 있다.(기본값은 Priority.DEBUG),

n         일반적으로 Log4j 는 어플리케이션이 초기화되는 동안 보통 설정파일을 읽어들여 한번만 설정된다.

n         appender 들은 singleton 이 아니라 추가된다는 것을 주의하라. appender 로부터 물려받는 것을 불가능하게 하기 위해 category cat.setAdditivity(false) 메소드를 사용하라.  그러면 로그 메세지는 해당 category에 특별히 설정된 appender 로만 보내질 것이다.

n         정적 메소드인 BasicConfigurator.configure() 메소드는 System.out.println(...) 구문이 그러하듯 콘솔에 로깅한다. 이 메소드는 루트 category ConsoleAppender 를 붙여 콘솔에 프린트 하는 기능을 가지고 있다. 출력은 %-4r [%t] %-5p %c %x - %m%n 으로 세팅된 PatternLayout 을 사용하여 구성된다. 이 메소드는 log4j.properties 파일을 사용하는 것이 더 간단하고 유연하기 때문에 그다지 필요하지 않다.

n         log4j 를 설정하는 기본 파일은 log4j.properties 이다. 이 파일을 어플리케이션의 클래스패스에 지정된 어느곳이든 위치시킨다.

n         Java 프로퍼티 파일에서 설정 정보를 읽어오려면 정적메소드 중 하나인 PropertyConfigurator.configure(...) 메소드를 사용하라. 이미 설정되어 있는 설정은 지워지지도 재설정되지도 않는다. 이 메소드를 사용하려면 정적메소드인 BasicConfigurator.resetConfiguration() 메소드를 먼저 호출하라. 이 메소드 사용은 코드를 길고 복잡하게 할 수도 있다. 초보자는 각각의 어플리케이션의 루트디렉토리에 log4j.properties 파일 하나만 위치시켜라.

n         참고 사이트 : http://logging.apache.org/log4j/docs/

 

[]

Trackback 0 Comment 0
2008/09/08 15:06

MultipartRequest 로 파일 업로드 하기

파일을 업로드 한다는 것은 원격의 사용자가 서버 측으로 네트워크를 통해 파일을 전송하는 일이다<?xml:namespace prefix = o ns = "urn:schemas-microsoft-com:office:office" />

보통 WEB 환경에서 사용자의 입력 값을 다른 페이지로 이동시키기 위해서는 Form 통한 데이터 전송을 하게 된다.

간단하게 사용자의 이름을 입력 받고 내용을 JSP 페이지나 기타 파일로 전송을 해야 때는 아래와 같은 HTML 폼이 작성이 되어야 것이다.

 

<Form action=receive.jsp Method=POST>

이름 : <Input type=TEXT name=userName> <BR>

<input type=SUBMIT value= >

</Form>


위의 파일을 실행하면 Action 태그에 정의된 페이지에서 사용자의 입력 값을 전송 받을 있게 된다. 하지만 File 경우는 그리 만만치 않다. 사용자가 입력하는 값은 Text 형태의 값이 대부분이고, 전송되는 File 형태는 Binary 타입이기 때문이다.

때문에 파일이 전송될 때에는 파일의 형태를 읽어 들일 있는 Servlet 또는 Java Bean 제작해야 한다.

 

아래는 파일전송을 위한 HTML 형태이다.

 

<!-- FileName : uploadForm.html  파일위치 c:\tomcat4/webapps/ROOT/uploadTest -->

<Form action=receive.jsp Method=POST enctype=multipart/form-data>

이름 : <Input type=TEXT name=userName> <BR>

파일 : <Input type=FILE name=userFile><BR>

<input type=SUBMIT value= >

</Form>

 

위의 파일을 보면 enctype 이라는 태그가 데이터를 전송하게 타입을 지정하는 부분이다.

아래쪽의 Input type=FILE 이라는 부분은 파일을 선택할 있는 창이 뜨게 되는 부분이다.

 

이렇게 전송되는 데이터를 receive.jsp 에서는 request 객체를 이용해서는 절대로 값을 가져오지 못한다.  Enctype 명시가 되면 넘어오는 파라메터들은 하나의 스트림을 통해 전송이 되게 된다. 그렇기 때문에 스트림으로 넘어오는 데이터를 각각의 구분자를 이용하여 짤라내야 한다.

그럼 데이터는 어떻게 받아낼까.

ServletRequest 인터페이스 에는 getInputStream() 이라는 메소드가 있다. 메소드는 클라이언트 측에서 전송되는 스트림을 서버측에서 받아줄 통로를 열어주는 메소드 이다. 위의 메소드를 통하여 사용자가 전송하는 스트림을 받아올 것이다.

아래는 데이터를 받아오는 receive.jsp 파일이다.

 

<%-- FileName : receive.jsp  SavePath : c:\tomcat4/webapps\ROOT\uploadTest --%>

<%@ page contentType=text/html; charset=euc-kr %>

<%@ page import=java.io.* %>

<%

         BufferedReader bufReader =

 new BufferedReader(new InputStreamReader(request.getInputStream()));

         String read = “”;

         while((read = bufReader.readLine()) != null){

                  out.print(read + <BR>);

}

bufReader.close();

%>

위의 파일을 해당 경로에 두고 브라우져에서 HTML 파일부터 실행 시키고 Text 형태의 파일을 전송해보자. JAVA SOURCE 파일도 괜찮다.   실행이 되었다면 아래와 같은 그림이 나오게 된다.

 

<?xml:namespace prefix = v ns = "urn:schemas-microsoft-com:vml" />

 

 

 

 

 

 

 

위의 그림을 자세히 보면 일정한 양식으로 입력 값이 전송된다는 것을 있다.

-----------------------------7d33d86502e0

위와 같은 부분을 Delimeter 라고 부른다 이렇게 넘어온 딜리미터를 이용하여 각각의 내용을 수동으로 잘라내야 한다.

처음 나오는 딜리미터 아래를 보면 Content-Desposition: form-data; name=userName  이라는 부분이 나오는 것을 있는데 이것은 HTML 에서 넘어오는 Form Data 구분해서 그에 상응하는 Parameter Name Parameter Value 나눠서 보여준다는 것을 있다.  물론 tornado 라는 것은 파라미터 값이 되겠다.

 

다시 딜리미터가 시작이 되고 아랫줄에 filename= 이라는 곳이 시작이 되는데 이곳부터 실제 파일이 시작이 되는 것이다. 현재 업로드 파일은 TEXT 형식이기 때문에 위와 같이 보여지지만 Binary 형태의 자료가 업로드가 된다면 이상한 문자들을 화면 가득 만날 것이다.

 

참고로 Binary 자료를 서버측에서 받으려 한다면 절대로 Reader 계열의 IO 클래스들을 사용해서는 안된다. Reader 계열 클래스 들은 문자를 읽어 들이는 스트림 이기 때문에 Binary 자료를 읽어 들이게 되면 제대로 업로드가 되리라는 보장이 없다.

 

위와 같이 복잡한 과정을 통해 업로드가 된다는 것을 알아보았다.

다시 한번 강조하지만 HTML Form 태그에 enctype 명시가 되면 절대로 Request 객체로 값을 받지 못한다는 것을 상기하기 바란다. 자료를 업로드 때에는 Method 속성이  POST 여야 한다는 것도 잊지 말자.(그렇다고 GET 방식은 절대로 못한다는 것은 아니다)

MultipartRequest 파일 업로드 하기

위와 같은 복잡한 과정을 거쳐 업로드를 하게 된다면 자바 IO 제대로 공부하지 않은 사람은 업로드 게시판은 절대 꿈도 것이다. 하지만 이렇게 복잡한 업로드를 간단히 해결해 주는 클래스들이 인터넷 상에 제공되고 있다.

우리가 사용할 MultipartRequest 라는 업로드 컴포넌트는 Oreilly 사에서 제공하는 컴포넌트이며 컴포넌트를 사용할 때에는 저작권이 따른다는 것을 알고 있기를 바란다.

또한 컴포넌트를 사용하려면 Oreilly 책을 구입해야 한다고 명시하고 있다.

 

위의 컴포넌트를 받으려면 아래의 웹사이트를 방문하여야 한다.

http://servlets.com/cos/index.html

 

위의 주소로 접속을 하고 화면을 아래로 스크롤 하게 되면 압축파일이 있는 표를 만나게 되는데

날짜를 확인하고 제일 최신 버전을 다운로드 받으면 된다.

참고로 현재 최신 버전은 2002 11 05 판이다.

 

cos-05Nov2002.zip 같은 파일을 찾았다면 다운로드를 하고 적당한 곳에 압축을 푼다.

압축을 풀면 아래와 같은 디렉토리가 생기게 된다.



 


디렉토리의 구조는 아래와 같다.

classes 디렉토리는  MultipartRequest 클래스 들이 위치한 디렉토리 이다.

Doc 디렉토리는 MultipartRequest API Document 들어있는 디렉토리 이다

Lib 디렉토리는 MultipartRequest 클래스들을 압축하여 놓은 Library 디렉토리이다.

Src 디렉토리는 Source 디렉토리 이다.

upload.war 파일은 아카이브 라는 형태의 파일로서 이것을 webapps 놓고 톰캣을 Restart 하게 되면 해당 war 파일이 압축이 풀리며 바로 사용 가능한 상태로 되는 파일형식이다. 파일을 이용하여 테스트를 있다.

 

Lib 디렉터리에 있는 cos.jar 이라는 파일을 톰캣 설치디렉토리의 common/lib 복사 한다. (c:/tomcat4/common/lib)

common/lib 위치 시켜야 하는데 이유는 톰캣이 처음 시작될 common/lib 있는 라이브러리를 읽어 들일 있게 해당 파일을 로딩하기 때문이다. JDBC 드라이버도 마찬가지로 이곳에 위치시켜야 한다.

Tomcat restart 하게 되면 cos.jar 파일에 들어있는 클래스들을 이용할 있게 된다.

 

본격적으로 업로드를 해보자.

 

위에서 작성한 receive.jsp 파일을 아래와 같이 작성한다.

<%-- FileName receive.jsp  SavePath c:/tomcat4/webapps/ROOT/uploadTest --%>

<%@ page contentType="text/html;charset=euc-kr" %>

<%@ page import="com.oreilly.servlet.MultipartRequest" %>

<%@ page import="com.oreilly.servlet.multipart.DefaultFileRenamePolicy" %>

<%@ page import="java.util.*" %>

<%@ page import="java.io.*" %>

 

<%

             String savePath="c:/tomcat4/webapps/ROOT/uploadTest";   // 저장할 디렉토리 (절대경로)

             int sizeLimit = 10 * 1024 * 1024 ;      // 파일업로드 용량 제한.. 10Mb

             try{

                           MultipartRequest multi = new MultipartRequest(request, savePath,

                                                                  sizeLimit,"euc-kr",new DefaultFileRenamePolicy());

                           String userName = multi.getParameter("userName");

                           String fileName= multi.getFilesystemName("userFile");

                           String originFileName = multi.getOriginalFileName("userFile");

 

                           if(fileName == null) { 

                                        out.print("파일이 업로드 되지 않았습니다!!");

                           } else {

                                        fileName=new String(fileName.getBytes("ISO-8859-1"),"euc-kr");

                                        out.print("User Name : " + multi.getParameter("userName") + "<BR>");

                                        out.print("getFilesystemName()  : " + fileName + "<BR>");

                                        out.print("getOriginalFileName() : " + originFileName +"<BR>");

                           } // end if

             } catch(Exception e) {

                           out.print(e.getMessage());

             }

%>

 

http://localhost:8080/uploadTest/uploadForm.html 부터 실행을 한다.

업로드 파일을 선택하고 전송 버튼을 누르게 되면 아래와 같은 그림이 나오게 된다.

 

 

 

 

 

 

다시 위의 과정을 반복해 본다. 아래와 같은 그림이 나온다.

 

 

 

 

 

이제 업로드 디렉터리를 보게 되면 아래와 같은 그림이 나오게 된다.

 

 

 

 

 

 

위의 결과치를 보게 되면 MultipartRequest 안에 들어있는 가지의 메소드의 용법을 있다.

String fileName= multi.getFilesystemName("userFile");

String originFileName = multi.getOriginalFileName("userFile");

 

getFilesystemName() 메소드는 서버측으로 실제 업로드 파일의 이름을 얻어오는 메소드 라는 것을 수가 있다.

getOriginalFileName() 메소드는 사용자가 업로드 실제 파일 이름을 얻어오는 메소드 이다.

그리고 중복 파일이 있을 경우에는 자동으로 파일이름 뒤에 중복된 숫자가 붙는다는 것을 있다.

이제 이렇게 업로드 파일의 이름을 데이터베이스에 입력을 하게 되면 자료실을 쉽게 구현 수가 있게 된다.

 

다음으로 알아볼 것은 업로드 파일의 사이즈를 알아내는 부분이다.

MultipartRequest 에는 파일의 사이즈를 반납해 주는 메소드는 없다. 하지만 File 객체 자체를 반납해 주는 메소드가 존재한다.

실제 파일객체를 받게 경우 File 객체에서 사용할 있는 모든 메소드를 사용할 있다는 장점이 있다.

파일의 사이즈는 아래와 같이 얻어오면 된다.

File f = multi.getFile("userFile");

같이 받아오면 된다.  이제 이렇게 받아온 파일을 통해 파일의 사이즈 각종 정보를 얻을 있게 된다.

 

 

아래의 코드를 receive.jsp 파일에 추가한다. 위치는 userFile 이름을 받아오는 아랫쪽에 위치 시키면 된다.

File f = multi.getFile(userFile);

int fileSize = (int)file.length();

out.print(<BR> 파일 사이즈 : + fileSize + <BR>);

사이즈가 출력이 되는 것을 있을 것이다. 참고로 int 형변환 하는 것은 File 객체의 length() 메소드의 반환값이 long 이기 때문에 형변환을 해준 것이다.

 

이제 여러 개의 파일을 업로드를 해보자. 이렇게 업로드 파일을 데이터 베이스의 테이블에 저장해 보도록 하자.

테이블의 구조는 아래와 같다. MySQL test 데이터베이스에 만들도록 하자.

 

Create table upTable(

             num int unsigned not null auto_increment primary key,

             name varchar(50) not null,

             subject varchar(200) not null,

             contents text not null,

             up1 varchar(50),

             up2 varchar(50),

             up3 varchar(50),

             size1 int,

             size2 int,

             size3 int,

);

 

먼저 파일을 선택할 있는 HTML 형식의 문서가 있어야 한다.

 

) multiUp.html 

<HTML>

<HEAD>

             <TITLE>JSP UPLOAD Test</TITLE>

<HEAD>

<BODY>

<FORM name="frm" method="POST" ACTION="multiUp.jsp" enctype="multipart/form-data">

<TABLE Border="1">

             <TR>

                           <TD>이름 <input type="text" name="userName"></td>

             </TR>

             <TR>

                           <TD>제목 <input type="text" name="subject" size=40></td>

             </TR>

             <TR>

                           <TD>내용<TextArea name="contents" rows="10" cols="60"></textArea></td>

             </TR>

             <TR>

                           <TD>파일 1  <input type="file" name="up1"> </TD>

             </TR>

             <TR>

                           <TD>파일 2  <input type="file" name="up2"> </TD>

             </TR>

             <TR>

                           <TD>파일 3  <input type="file" name="up3"> </TD>

             </TR>

             <TR>

                           <TD> <INPUT TYPE="SUBMIT" value=" "> </TD>

             </TR>

</TABLE>

</FORM>

</BODY>

</HTML>

 

위의 파일에서 주의 하여야 것은 enctype=multipart/form-data 부분이다. 다시 강조하지만 enctype 부분이 생략되면 업로드가 안된다는 것을 알고 있기 바란다.

이제 위의 HTML 폼에서 넘어가는 파일들과 파라메터들을 받아서 저장해줄 JSP 파일이 필요하다.

아래의 파일이 역할을 해주는 파일이다.

) multiUp.jsp

<%-- FileName : multiUp.jsp   savePath : c:/tomcat4/webapps/ROOT/uploadTest  --%>

<%@ page contentType="text/html; charset=euc-kr" %>

<%@ page import="com.oreilly.servlet.MultipartRequest" %>

<%@ page import="com.oreilly.servlet.multipart.DefaultFileRenamePolicy" %>

<%@ page import="java.util.*" %>

<%@ page import="java.sql.*" %>

<%@ page import="java.io.*" %>

 

<%

 

             String savePath = "c:/tomcat4/webapps/ROOT/uploadTest"; // 업로드 경로

             int sizeLimit = 5 * 1024 * 1024;

 

             MultipartRequest multi = new MultipartRequest(request, savePath, sizeLimit, new DefaultFileRenamePolicy());

             /*

                오라클의 경우 아래와 같이 MultipartRequest 생성자 호출시에 한글이라는 것을 명시해야 한글이 안깨진다.

                MultipartRequest multi =

                          new MultipartRequest(request, savePath, sizeLimit, "euc-kr", new DefaultFileRenamePolicy());

             */

            

             // 넘어오는 파라메터.

             String userName = multi.getParameter("userName");

             String subject = multi.getParameter("subject");

             String contents = multi.getParameter("contents");

            

             // 넘어오는 파일 이름

             String fileName1 = multi.getFilesystemName("up1");

             String fileName2 = multi.getFilesystemName("up2");

             String fileName3 = multi.getFilesystemName("up3");

 

             // 파일  사이즈 받기

             int size1 = (int)(new File(savePath + "/" + fileName1).length());

             int size2 = (int)(new File(savePath + "/" + fileName2).length());

             int size3 = (int)(new File(savePath + "/" + fileName3).length());

            

             // 데이터베이스 접속

             Class.forName("org.gjt.mm.mysql.Driver").newInstance();

             Connection conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/test","root","123456");

 

             String query = "insert into upTable values(null, ?, ?, ?, ?, ?, ?, ?, ?, ?)";

             PreparedStatement pstmt = conn.prepareStatement(query);

             pstmt.setString(1, userName);

             pstmt.setString(2, subject);

             pstmt.setString(3, contents);

             pstmt.setString(4,  fileName1);