저도 참석하기위해 사전등록 마쳤습니다.

많이들 참가하셔서 자바의 역사에 발자국들을 찍으시기를 ^^

 

Posted by 마음소리

자바로 zip 파일을 다룰때(압축 풀기/압축하기) 영문이름으로 된 파일은 정상적으로 잘 되지만

한글이름으로된 파일을 다룰때에는 Exception이 발생한다.

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


import java.io.File;
import java.io.FileInputStream;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;


public class ZipTest {

    public static void main(String[] args) throws Exception {
        File file = new File("e:/교육자료/이클립스단축키.zip");
        ZipInputStream in = new ZipInputStream(new FileInputStream(file));
        ZipEntry entry = null;
        while((entry = in.getNextEntry()) != null ){
            System.out.println(entry.getName()+" 파일이 들어 있네요.");
        }
    }

}


위 예제는 이클립스단축키.zip 라는 zip 파일안에 어떤 파일들이 압축되어 있는지 단순하게 리스트를 출력해보는 프로그램이다. 이클립스단축키.zip 파일안에는 다음과 같은 파일 2개가 압축되어 있다.

Keyboard_shortcuts.pdf

이클립스단축키.txt


이 프로그램을 실행시켜보면 아래와 같은 에러가 발생한다.


Keyboard_shortcuts.pdf 파일이 들어 있네요.
java.lang.IllegalArgumentException
 at java.util.zip.ZipInputStream.getUTF8String(ZipInputStream.java:284)
 at java.util.zip.ZipInputStream.readLOC(ZipInputStream.java:237)
 at java.util.zip.ZipInputStream.getNextEntry(ZipInputStream.java:73)
 at ZipTest.main(ZipTest.java:29)
Exception in thread "main"


[Keyboard_shortcuts.pdf] 파일명을 처리할때는 문제가 없다가 [이클립스단축키.txt] 처럼 한글이름으로 된 파일명을 다룰때는 Exception 이 발생함을 알 수 있는데, 이문제는 java.util.zip 패키지의 한글처리 인코딩로직의 버그이다. 하지만 한글 인코딩 문제는 의외로 아주 간단하게 해결된다.


Jochen Hoenicke 가 만든 jazzlib.jar 라이브러리를 다운받아 클래스패스에 추가하면 된다. 소스는 전혀 수정할 필요가 없고 단지 import 문만 아래와 같이 수정해 주면 된다.


import java.io.File;
import java.io.FileInputStream;
import net.sf.jazzlib.ZipEntry;
import net.sf.jazzlib.ZipInputStream;


public class ZipTest {

    public static void main(String[] args) throws Exception {
        File file = new File("e:/교육자료/이클립스단축키.zip");
        ZipInputStream in = new ZipInputStream(new FileInputStream(file));
        ZipEntry entry = null;
        while((entry = in.getNextEntry()) != null ){
            System.out.println(entry.getName()+" 파일이 들어 있네요.");
        }
    }

}


다시 프로그램을 실행시켜 보면 아래와 같이 정상적인 결과를 확인할 수 있다.

Keyboard_shortcuts.pdf 파일이 들어 있네요.
이클립스단축키.txt 파일이 들어 있네요.


jazzlib.jar 파일은 인코딩 문제를 해결한 라이브러리이므로 압축할때와 압축을 풀때 모두 잘 작동한다.


Reference is jazzlib


Posted by 마음소리

saveToken() Struts Framework

2008/01/29 02:24

복사 http://blog.naver.com/ceresrits/40047089002



스트럿츠에서 중복처리를 위해 사용하는 saveToken() 에대해서 간략히 알아보자

원리는 간단한듯하면서 복잡미묘? ㅋㅋ

요청(request)에 값을 실어서 그값을 검사해 같은값이 들어오는지 다른값이 들어오는지에 따라 중복여부를 처리한다.

코드로 볼까나?


아 우선 순서를 알아보자 흔히 MVC2 Parttern에서 중복을 처리할때 스트러츠 프레임 워크는

두번의 액션(control)을 거친다.


request         firstAction                      viewpage(jsp)                                         두번째액션

(요청)             (여기서 값                      두번째 액션으로                                      넘어온값을

-------->         을 심는다) --------->   값을 전달하기위해 hidden--------------> 체크하여 중복

                                                          값을 넘긴다                                           확인을 한다.


첫번째 액션

public class firstAction()extends DispatchAction{

    public ActionForward first(.......){

          ActionForward forward=null;

              saveToken(request);//요문장이 요청에 값을 담는문장이다. 그대로 사용하면 된다.

              forward=mapping.findForward(String name);//뷰페이지로 이동하기위한 포워드를 설정

            return forward;

     }

}


view page(jsp)

일반폼의 경웨는 폼안에 히든값을 심어서 두번째 페이지로 토큰을 전달한다. 이것도 이형식 그대로

ctrl+c  ctrl+v해서 쓰자

<form ...action="second.do?method=second" > //액션의 맵핑은 *.do라고 가정하자

<input type=hidden name="org.apache.struts.taglib.html.TOKEN" value="<%=session.
getAttribute("org.apache.struts.action.TOKEN")%>" >
  .

  .

  .

</form>


두번째 액션

public class SecondAction() extedns DispatchAction{

    public ActionForwad Second(....){

           ActionForward forward=null;

              if(!isTokenValid(request)){
                   forward=mapping.findForward("re_list");//값이 중복이므로 포워드한다.
              }else{
                            resetToken(request);

                                  .

                                  .//중복이 아니면 작업내역
                             forward=mapping.findForward("re_list");//작업을 마치고 포워드 설정
                             saveToken(request);//새로운 값을 심어 다음의 과정을 준비한다.(f5 key예방)


            return forward;

       }


}


이상으로 saveToken()을 이용한 중복명령 방지에 대해서 알아보았다.

 
Posted by 마음소리
I. POI 란?
 
일반적으로 POI가 엑셀파일을 쓰는 컴퍼넌트로 알려져 있으나 POI는 프로젝트 이름입니다.
즉 POI는 Microsoft Format File을 액세스 할 수 있는 API를 제공합니다. (한마디로 자바에서 MS파일을 읽고 쓸수있도록 지원합니다.)
 
POI안에는 여러 컴퍼넌트들이 있습니다.
POIFS
Microsoft의 OLE2 포맷 형식의 문서를 자바로 읽고 쓸수 있는 컴퍼넌트입니다
기본적으로 POI의 모든 컴퍼넌트들이 POIFS를 사용합니다.
HSSF
Microsoft의 엑셀파일을 읽고 쓸수 있도록 지원하는 컴퍼넌트입니다.
HWPF
Microsoft의 워드파일을 읽고 쓸수 있도록 지원하는 컴퍼넌트입니다.
이 컴퍼넌트는 디자인 초기단계입니다.
HPSF
Microsoft의 OLE2 포맷 형식의 문서 속성을 어플리케이션에서 사용 할수 있도록 지원하는 컴퍼넌트입니다.
현재 읽기 기능만 제공합니다
 
워드파일을 핸들링 하는 HWPF는 초기단계라 사용을 못하지만 기대는 되는군요 ^^
 
ps. 영어사전을 찾아보니 poi는 하와이의 토란 요리를 뜻하더군요.
우리나라말로 하니 자카르타 토란 프로젝트 쯤 될라나? ㅎㅎ
 
 
II. 다운로드 및 설치
 
다운로드 받으러 갑시다~!
현재 2.5.1버젼입니다.
다운받은 파일을 압축을 풀면 *.jar 파일들이 있을겁니다 이 파일들을 자신의 어플리케이션 /lib/에 복사합시다
 
 
 
III. Formula(수식) 지원에 관해..
 
엑셀을 읽고 쓸때 수식을 지원합니다.
org.apache.poi.hssf.usermodel.HSSFCell의 setCellFormula("formulaString") 메쏘드는 스프레드시트에 수식을 추가하는데 사용되며 getCellFormula() 메쏘드는 수식을 대표하는 문자열을 해석하는데 사용됩니다. 하지만 엑셀에서 사용하는 수식을 모두 사용 할 수는 없습니다.
 
지원되는 부분
-. 셀 참조, 시트참조, 지역참조
-. 상대적 혹은 절대적 참조
-. 수연산 및 논리연산
-. 시트 혹은 매크로 함수
-. 수식 결과값 반환
 
부분적 지원
문자열을 포함하는 수식을 해석할 수는 있지만 문자열값을 반환하는 수식은 아직 지원하지 않습니다.

지원되지 않는 부분
-. 배열 수식
-. 1진법 수식
-. 3D 참조
-. 에러 값 (cells containing #REF's or #VALUE's)

 
IV. 기본객체

가장 기본이되는 객체가 다음 4가지 입니다
이름에서 무엇을 뜻하는지 대강 짐작 할 수 있겠죵?
 
HSSFWorkbook - 엑셀 워크북을 말합니다.
HSSFSheet - 엑셀 쉬트를 나타냅니다.
HSSFRow - 엑셀에서 특정 행입니다.
HSSFCell - 엑셀에서 특정 행에대한 특정 셀입니다
 
위 4가지 객체는 앞으로 계속 나올겁니다. 눈여겨 미리 봐 둡시다. @.@
 
 
V. 엑셀 읽기 예제
 
POSFS을 이용하여 엑셀 워크북을 생성합니다.
 
POIFSFileSystem fs = new POIFSFileSystem(new FileInputStream("excelfile.xls"));
HSSFWorkbook workbook = new HSSFWorkbook(fs);
 
 
생성된 워크북을 이용하여 시트 수만큼 돌면서 엑셀 시트 하나씩을 생성합니다.
 
int sheetNum = workbook.getNumberOfSheets();
for (int k = 0; k < sheetNum; k++) {
   System.out.println("Sheet Number : "+k);
   System.out.println(Sheet Name : " + workbook.getSheetName(k));
   HSSFSheet sheet = workbook.getSheetAt(k);
}
 
 
생성된 시트를 이용하여 그 행의 수만큼 돌면서 행을 하나씩 생성합니다.
 
int rows = sheet.getPhysicalNumberOfRows();
for (int r = 0; r < rows; r++) {
   HSSFRow row   = sheet.getRow(r);
   System.out.println("Row : "+row.getRowNum());
}
 
 
역시나 생성된 행을 이용하여 그 셀의 수만큼 돌면서 셀을 하나씩 생성합니다.
 
int cells = row.getPhysicalNumberOfCells();
for (short c = 0; c < cells; c++) {              <--!! short 형입니다. 255개가 max!
    HSSFCell cell  = row.getCell(c);
    int celltype = cell.getCellType();
    ...
}
셀을 생성하여 셀 타입에 따라 처리를 해주면 끝~
 
주의사항
만약 엑셀에서 A열에 아무런 값이 없으면 그 행은 읽지 못합니다.
행을 읽지 못하니 셀또한 처리 할 수 없습니다
 
 
VI. 엑셀읽기 샘플소스
 
샘플 데이터
 
 
 
 
 
 
 
A열은 B열에 대한 셀 타입을 나타내며 C열은 D열에대한 셀 타입을 나타냅니다.
즉 B:1 의 123456의 셀 타입은 A:1 일반 이라는 것이며 마찬가지로
D:1의 2005-02-09의 셀타입은 C:1 사용자정의로 세팅하였다는 겁니다
 
이 엑셀의 데이터를 다음 소스로 읽어 보겠습니다.
 
<%@ page
language="java"
contentType="text/html;charset=euc-kr"
import="java.io.*,
 org.apache.poi.poifs.filesystem.POIFSFileSystem,
 org.apache.poi.hssf.record.*,
 org.apache.poi.hssf.model.*,
 org.apache.poi.hssf.usermodel.*,
 org.apache.poi.hssf.util.*" %>
 
<html>
<head><title>Read example</title></head>
<body>
<%
  String excelfile = "C:Tomcat 5.0webappsROOTexample.xls";
  try {
       POIFSFileSystem fs = new POIFSFileSystem(new FileInputStream(excelfile));
 
       //워크북을 생성!               
       HSSFWorkbook workbook = new HSSFWorkbook(fs);
       int sheetNum = workbook.getNumberOfSheets();
 
       for (int k = 0; k < sheetNum; k++) {
 
            //시트 이름과 시트번호를 추출
%>
            <br><br>
            Sheet Number <%= k %> <br>
            Sheet Name <%= workbook.getSheetName(k) %><br>
<%
            HSSFSheet sheet = workbook.getSheetAt(k);
            int rows = sheet.getPhysicalNumberOfRows();
 
            for (int r = 0; r < rows; r++) {
 
                // 시트에 대한 행을 하나씩 추출
                HSSFRow row   = sheet.getRow(r);
                if (row != null) {
                     int cells = row.getPhysicalNumberOfCells();
%>
                     ROW  <%= row.getRowNum() %> <%=cells%></b><br>
<%
                     for (short c = 0; c < cells; c++) {
 
                         // 행에대한 셀을 하나씩 추출하여 셀 타입에 따라 처리
                         HSSFCell cell  = row.getCell(c);
                         if (cell != null) {
                              String value = null;
                              switch (cell.getCellType()) {
                                   case HSSFCell.CELL_TYPE_FORMULA :
                                       value = "FORMULA value=" + cell.getCellFormula();
                                        break;
                                   case HSSFCell.CELL_TYPE_NUMERIC :
                                       value = "NUMERIC value=" + cell.getNumericCellValue(); //double
                                       break;
                                  case HSSFCell.CELL_TYPE_STRING :
                                       value = "STRING value=" + cell.getStringCellValue(); //String
                                       break;
                                  case HSSFCell.CELL_TYPE_BLANK :
                                      value = null;
                                     break;
                                 case HSSFCell.CELL_TYPE_BOOLEAN :
                                     value = "BOOLEAN value=" + cell.getBooleanCellValue(); //boolean
                                    break;
                                case HSSFCell.CELL_TYPE_ERROR :
                                     value = "ERROR value=" + cell.getErrorCellValue(); // byte
                                     break;
                                default :
                             }
%>        
                          <%= "CELL col=" + cell.getCellNum() + " VALUE=" + value %> <br>
<%
                        }
                    }
                }
            }
       }
   } catch (Exception e) {
%>
       Error occurred:  <%= e.getMessage() %>
<%  
       e.printStackTrace();
    }
%>

</body>
</html>
 
위 소스의 결과입니다.
 
Sheet Number 0
Sheet Name 한글
ROW 0 4
CELL col=0 VALUE=STRING value=일반
CELL col=1 VALUE=NUMERIC value=123456.0
CELL col=2 VALUE=STRING value=사용자정의
CELL col=3 VALUE=NUMERIC value=38392.0
ROW 1 4
CELL col=0 VALUE=STRING value=숫자
CELL col=1 VALUE=NUMERIC value=123456.0
CELL col=2 VALUE=STRING value=날짜 (yy-m-d h:mm)
CELL col=3 VALUE=NUMERIC value=38393.0
ROW 2 4
CELL col=0 VALUE=STRING value=통화
CELL col=1 VALUE=NUMERIC value=123456.0
CELL col=2 VALUE=STRING value=날짜 (yy年 mm月 dd日)
CELL col=3 VALUE=NUMERIC value=38394.0
ROW 3 4
CELL col=0 VALUE=STRING value=텍스트
CELL col=1 VALUE=NUMERIC value=123456.0
CELL col=2 VALUE=STRING value=날짜 (yyyy년 mm월 dd일)
CELL col=3 VALUE=NUMERIC value=38395.0

 
결과를 보니 사용자가 지정한 셀 타입에 관계없이
숫자관련 셀은 POI에서 모두 숫자 타입으로 인식해 버렸습니다.
날짜 역시 지정한 셀 타입에 관계없이 모두 숫자 타입으로 인식해 버리는군요!
그럼 어떻게 날짜를 제대로 표현할까요?
날짜 타입을 제대로 나타내기 위해서는 날짜 Cell에는 getDateCellValue()를 사용하면
정상적으로 처리 할 수 있습니다.
SimpleDateformat sdf = new SimpleDateformat("yyyy-MM-dd hh:mm");
String date = sdf.format(cell.getDateCellValue());
등을 이용하면 나타내고자 하는 알짜를 표현 하기 더 쉽겠지요
나머지 수식을 가져 올때도 마찬가지입니다. 이런 사항을 도표로 나타내보았습니다.
 
org.apache.poi.hssf.usermodel.HSSFCell 에는 모두 6가지의 Cell Type이 있는데,
cell.getCellType()을 하면 그 셀의 반환값을 알 수 있으며 그에 상응하는 static 필드타입은 다음과 같습니다.
 
셀타입 필드타입
함수
함수반환값
0 CELL_TYPE_NUMERIC
getNumericCellValue()
-> 숫자 타입일때
getDateCellValue()
-> 날짜 타입일때
double
 
Date
 
1 CELL_TYPE_STRING
getStringCellValue()
String
2 CELL_TYPE_FORMULA
getCellFormula()
-> 수식자체를 가져올때
getNumericCellValue()
-> 수식 반환값이 숫자일때
getStringCellValue()
-> 수식 반환값이 문자일때
String
 
double
 
String
3 CELL_TYPE_BLANK
 
 
4 CELL_TYPE_BOOLEAN
getBooleanCellValue()
boolean
5 CELL_TYPE_ERROR
getErrorCellvalue()
byte
 
이번시간에는 POI 프로젝트를 이용하여 엑셀 파일을 읽어보았습니다.
다음 시간에는 엑셀파일에 쓰는 핸드링을 해 보도록 하지요~
 
Posted by 마음소리
VII. 엑셀 쓰기예제
 
쓰기도 역시 읽기와 비슷합니다.
엑셀 워크북을 생성합니다. 행과 셀을 생성하려면 당연한 절차겠죠?
HSSFWorkbook workbook = new HSSFWorkbook();
 
시트를 생성합니다.
시트명을 파라미터로 바로 생성 합니다.
HSSFSheet sheet = workbook.createSheet("sheet name");
 
만약 한글로 시트명을 만들려면 다음과 같이 인코딩이 필요합니다.
HSSFSheet sheet = workbook.createSheet();
workbook.setSheetName( 0 , "한글" , HSSFWorkbook.ENCODING_UTF_16 );
 
셀에 사용할 스타일을 미리 생성해 둡니다.
HSSFCellStyle style = wb.createCellStyle();
style.setBorderBottom(HSSFCellStyle.BORDER_THIN);
style.setBottomBorderColor(HSSFColor.BLACK.index);
style.setBorderLeft(HSSFCellStyle.BORDER_THIN);
style.setLeftBorderColor(HSSFColor.GREEN.index);
style.setBorderRight(HSSFCellStyle.BORDER_THIN);
style.setRightBorderColor(HSSFColor.BLUE.index);
style.setBorderTop(HSSFCellStyle.BORDER_MEDIUM_DASHED);
style.setTopBorderColor(HSSFColor.BLACK.index);
등 여러가지 스타일을 만들 수 있습니다.
 
스타일은 다음 주소를 참고하세요
 
로우를 하나 생성합니다.
HSSFRow row = sheet.createRow(0);
 
셀츨 하나 생성하여 스타일을 주고 값을 입력합니다.
HSSFCell cell = row.createCell((short)0);
cell.setCellStyle(style);
cell.setCellValue("jakarta project!");
 
만약 한글을 입력한다면 인코딩 해야 하며 값 세팅전에 해야 합니다.
cell.setEncoding(HSSFCell.ENCODING_UTF_16);  //한글 처리
cell.setCellStyle(style);
cell.setCellValue("자카드타 프로젝트!");
 
모든 셀이 다 입력되었으면 파일을 만듭니다.
FileOutputStream fs = new FileOutputStream("excelfile.xls");
workbook.write(fs);
fs.close();
 
 
VIII. 쓰기샘플 소스
 
<%@ page language="java" contentType="text/html;charset=euc-kr" %>
<%@ page import="java.io.*" %>
<%@ page import="org.apache.poi.poifs.dev.*" %>
<%@ page import="org.apache.poi.hssf.record.*" %>
<%@ page import="org.apache.poi.hssf.record.formula.*" %>
<%@ page import="org.apache.poi.hssf.model.*" %>
<%@ page import="org.apache.poi.hssf.usermodel.*" %>
<%@ page import="org.apache.poi.hssf.util.*" %>

<html>
<body>
<%

    String filepath = "C:Tomcat 5.0webappsROOTwrite.xls";
 
    try {
        String[] cell_value = {"자카르타","프로젝트","www.jakartaproject.com"};
 
        HSSFWorkbook workbook = new HSSFWorkbook();

        HSSFSheet sheet = workbook.createSheet();
        workbook.setSheetName(0 , "한글명" ,HSSFWorkbook.ENCODING_UTF_16);
 
        HSSFCellStyle style = workbook.createCellStyle();
        style.setBorderBottom(HSSFCellStyle.BORDER_THIN);
        style.setBottomBorderColor(HSSFColor.BLACK.index);
        style.setBorderLeft(HSSFCellStyle.BORDER_THIN);
        style.setLeftBorderColor(HSSFColor.GREEN.index);
        style.setBorderRight(HSSFCellStyle.BORDER_THIN);
        style.setRightBorderColor(HSSFColor.BLUE.index);
        style.setBorderTop(HSSFCellStyle.BORDER_MEDIUM_DASHED);
        style.setTopBorderColor(HSSFColor.BLACK.index);           
 
        HSSFRow row = sheet.createRow(0);
        for (int i = 0 ; i < cell_value.length; i++){
            HSSFCell cell = row.createCell((short)i);
            cell.setEncoding(HSSFCell.ENCODING_UTF_16);
            cell.setCellStyle(style);
            cell.setCellValue(cell_value[i]);
        }
           
        FileOutputStream fs = null;
        try {
            fs = new FileOutputStream(filepath);
            workbook.write(fs);
        } catch (Exception e) {
        } finally {
            if (fs != null) fs.close();
        }
       
    } catch (Exception e) {
%>
        Error occurred:  <%= e.getMessage() %>
<%  
        e.printStackTrace();
    }   
   
%>
</body>
</html>
 
 
자 결과화면 입니다.
 
 
 
 
성공!
 
위의 소스를 기본으로 한다면 그리 어렵지 않을겁니다 ^^
 
출처 : http://www.jakartaproject.com/article/jakarta/1109322375613
Posted by 마음소리
IX. Cell을 좀더 유연하게!
 
1. Date타입 셀 만들기
소스
HSSFWorkbook wb = new HSSFWorkbook();
HSSFSheet sheet = wb.createSheet("new sheet");
 
HSSFRow row = sheet.createRow((short)0);
 
//처음셀은 style없이 그냥 new Date()로 입력
HSSFCell cell = row.createCell((short)0);
cell.setCellValue(new Date());
 
//두번째 셀은 "m/d/yy h:mm"으로 포맷하여 날짜를 입력
HSSFCellStyle cellStyle = wb.createCellStyle();
cellStyle.setDataFormat(HSSFDataFormat.getBuiltinFormat("m/d/yy h:mm"));
cell = row.createCell((short)1);
cell.setCellValue(new Date());
cell.setCellStyle(cellStyle);
 
FileOutputStream fileOut = new FileOutputStream("workbook.xls");
wb.write(fileOut);
fileOut.close();
 
결과
 
 
 
 
 
HSSFDateFormat이 지원하는 날짜 포맷
cellStyle.setDataFormat(HSSFDataFormat.getBuiltinFormat("m/d/yy h:mm")); 에서 다음과 같이 포맷을 정할 수 있다 (현재시간은 2005년 3월 14일 0시 52분 17초.. 헛 화이트데이 --;)

 
 
2. Cell의 Align속성
소스
HSSFWorkbook wb = new HSSFWorkbook();
HSSFSheet sheet = wb.createSheet("new sheet");
HSSFRow row = sheet.createRow((short) 2);
 
//HSSFCellStyle의 여러가지 align속성
createCell(wb, row, (short) 0, HSSFCellStyle.ALIGN_CENTER);
createCell(wb, row, (short) 1, HSSFCellStyle.ALIGN_CENTER_SELECTION);
createCell(wb, row, (short) 2, HSSFCellStyle.ALIGN_FILL);
createCell(wb, row, (short) 3, HSSFCellStyle.ALIGN_GENERAL);
createCell(wb, row, (short) 4, HSSFCellStyle.ALIGN_JUSTIFY);
createCell(wb, row, (short) 5, HSSFCellStyle.ALIGN_LEFT);
createCell(wb, row, (short) 6, HSSFCellStyle.ALIGN_RIGHT);

FileOutputStream fileOut = new FileOutputStream("workbook.xls");
wb.write(fileOut);
fileOut.close();

 
public void createCell(HSSFWorkbook wb, HSSFRow row, short column, short align)
{
    HSSFCell cell = row.createCell(column);
    cell.setCellValue("Align It");
    HSSFCellStyle cellStyle = wb.createCellStyle();
    cellStyle.setAlignment(align);
    cell.setCellStyle(cellStyle);
}
 
결과
 
 
 
 
 
 
 
3. Cell의 Border 속성
소스
HSSFWorkbook wb = new HSSFWorkbook();
HSSFSheet sheet = wb.createSheet("new sheet");

HSSFRow row = sheet.createRow((short) 1);
HSSFCell cell = row.createCell((short) 1);
cell.setCellValue(4);

// Cell의 Border 속성
HSSFCellStyle style = wb.createCellStyle();
style.setBorderBottom(HSSFCellStyle.BORDER_THIN);
style.setBottomBorderColor(HSSFColor.BLACK.index);
style.setBorderLeft(HSSFCellStyle.BORDER_THIN);
style.setLeftBorderColor(HSSFColor.GREEN.index);
style.setBorderRight(HSSFCellStyle.BORDER_THIN);
style.setRightBorderColor(HSSFColor.BLUE.index);
style.setBorderTop(HSSFCellStyle.BORDER_MEDIUM_DASHED);
style.setTopBorderColor(HSSFColor.BLACK.index);
cell.setCellStyle(style);

FileOutputStream fileOut = new FileOutputStream("workbook.xls");
wb.write(fileOut);
fileOut.close();
 
결과
 
 
 
 
 
 
 
HSSFCellStyle
HSSFCellStyle에는 다음과 같은 static 멤버변수가 존재합니다
ALIGN_CENTER center horizontal alignment
ALIGN_CENTER_SELECTION center-selection? horizontal alignment
ALIGN_FILL fill? horizontal alignment
ALIGN_GENERAL general (normal) horizontal alignment
ALIGN_JUSTIFY justified horizontal alignment
ALIGN_LEFT left-justified horizontal alignment
ALIGN_RIGHT right-justified horizontal alignment
ALT_BARS Wide dots
BIG_SPOTS Large spots
BORDER_DASH_DOT dash-dot border
BORDER_DASH_DOT_DOT dash-dot-dot border
BORDER_DASHED dash border
BORDER_DOTTED hair-line border
BORDER_DOUBLE double-line border
BORDER_HAIR dot border
BORDER_MEDIUM Medium border
BORDER_MEDIUM_DASH_DOT medium dash-dot border
BORDER_MEDIUM_DASH_DOT_DOT medium dash-dot-dot border
BORDER_MEDIUM_DASHED Medium dashed border
BORDER_NONE No border
BORDER_SLANTED_DASH_DOT slanted dash-dot border
BORDER_THICK Thick border
BORDER_THIN Thin border
BRICKS Brick-like layout
DIAMONDS Diamonds
FINE_DOTS Small fine dots
NO_FILL No background
SOLID_FOREGROUND Solidly filled
SPARSE_DOTS Sparse dots
SQUARES Squares
THICK_BACKWARD_DIAG Thick backward facing diagonals
THICK_FORWARD_DIAG Thick forward facing diagonals
THICK_HORZ_BANDS Thick horizontal bands
THICK_VERT_BANDS Thick vertical bands
THIN_BACKWARD_DIAG Thin backward diagonal
THIN_FORWARD_DIAG Thin forward diagonal
THIN_HORZ_BANDS Thin horizontal bands
THIN_VERT_BANDS Thin vertical bands
VERTICAL_BOTTOM bottom-aligned vertical alignment
VERTICAL_CENTER center-aligned vertical alignment
VERTICAL_JUSTIFY vertically justified vertical alignment
VERTICAL_TOP top-aligned vertical alignment
 
 
4. Cell의 색갈 채우기
소스
HSSFWorkbook wb = new HSSFWorkbook();
HSSFSheet sheet = wb.createSheet("new sheet");
HSSFRow row = sheet.createRow((short) 1);
 
HSSFCellStyle style = wb.createCellStyle();
// 아쿠아색을 배경으로 하고
style.setFillBackgroundColor(HSSFColor.AQUA.index);
//채움 스타일은 큰 점으로 한다
style.setFillPattern(HSSFCellStyle.BIG_SPOTS);
HSSFCell cell = row.createCell((short) 1);
cell.setCellValue("X");
cell.setCellStyle(style);
 
style = wb.createCellStyle();
// 오렌지색으로 전경으로 하고
style.setFillForegroundColor(HSSFColor.ORANGE.index);
// 채움 스타일은 SOLID_FOREGROUND로 한다
style.setFillPattern(HSSFCellStyle.SOLID_FOREGROUND);
cell = row.createCell((short) 2);
cell.setCellValue("X");
cell.setCellStyle(style);
 
FileOutputStream fileOut = new FileOutputStream("workbook.xls");
wb.write(fileOut);
fileOut.close();
 
결과
 
 
 
 
 
 
HSSFColor 정리!
 

 
 
5. Cell 병합
소스
HSSFWorkbook wb = new HSSFWorkbook();
HSSFSheet sheet = wb.createSheet("new sheet");
HSSFRow row = sheet.createRow((short) 1);
HSSFCell cell = row.createCell((short) 1);
cell.setCellValue("This is a test of merging");
sheet.addMergedRegion(new Region(1,(short)1,1,(short)2));
 
FileOutputStream fileOut = new FileOutputStream("workbook.xls");
wb.write(fileOut);
fileOut.close();
 
결과
 
 
 
 
 
 
 
Region
특정셀을 합칠 때는 HSSFSheet의 addMergedRegion(Region region)와
합칠 셀의 영역을 나타내는 Region을 사용한다.
Region region = new (int 시작ROW, short 시작COL, int 종료ROW, short 종료COL);
 
 
6. Cell에 폰트 설정하기
소스
HSSFWorkbook wb = new HSSFWorkbook();
HSSFSheet sheet = wb.createSheet("new sheet");
HSSFRow row = sheet.createRow((short) 1);
 
// 폰트 높이는 24, 폰트 종류는 Courier New, 이탈릭체로 설정한다
HSSFFont font = wb.createFont();
font.setFontHeightInPoints((short)24);
font.setFontName("Courier New");
font.setItalic(true);
font.setStrikeout(true);
 
// 설정한 폰트를 스타일에 적용한다
HSSFCellStyle style = wb.createCellStyle();
style.setFont(font);
 
// Cell에 스타일을 적용한다
HSSFCell cell = row.createCell((short) 1);
cell.setCellValue("This is a test of fonts");
cell.setCellStyle(style);
 
FileOutputStream fileOut = new FileOutputStream("workbook.xls");
wb.write(fileOut);
fileOut.close();
 
결과
 
 
 
 
 
 
 
Posted by 마음소리
POI 3.0 부터 드디어 이미지를 지원하는군요.
아직 알파버젼이구요 http://www.apache.org/dyn/closer.cgi/jakarta/poi/ 에서 최신버젼을 다운 받을 수 있습니다
 
최신링크
 
아래 소스는 OKJSP의 "이루"님이 작성한 소스입니다
 
import java.util.*;
import java.io.*;
import org.apache.poi.hssf.usermodel.*;
import org.apache.poi.poifs.filesystem.*;
import org.apache.poi.poifs.filesystem.POIFSFileSystem.*;
import org.apache.poi.poifs.eventfilesystem.*;

public class InsertImage {
    public static void main(String[] args) throws IOException {
        POIFSFileSystem fs      = new POIFSFileSystem(new FileInputStream("/xxx/test.xls")); // 원본엑셀파일
        HSSFWorkbook    wb      = new HSSFWorkbook(fs);
        HSSFSheet       sheet   = wb.getSheetAt(0);
        drawSheet5( sheet, wb );
        // Write the file out.
        FileOutputStream fileOut = new FileOutputStream("/xxx/img_work.xls"); // 이미지 삽입된 엑셀파일
        wb.write(fileOut);
        fileOut.close();
    }
 
    private static void drawSheet5( HSSFSheet sheet, HSSFWorkbook wb ) throws IOException {
        // Create the drawing patriarch.  This is the top level container for
        // all shapes. This will clear out any existing shapes for that sheet.
        HSSFPatriarch patriarch = sheet.createDrawingPatriarch();
        HSSFClientAnchor anchor;
        anchor = new HSSFClientAnchor(0,0,0,255,(short)1,4,(short)2,4); // 이미지 크기조절은 여기서..
        anchor.setAnchorType( 2 );
        patriarch.createPicture(anchor, loadPicture( "/xxx/okjsp.jpg", wb )); // 삽입 할 이미지
    }
 
    private static int loadPicture( String path, HSSFWorkbook wb ) throws IOException {
        int pictureIndex;
        FileInputStream fis = null;
        ByteArrayOutputStream bos = null;
        try {
            fis = new FileInputStream( path);
            bos = new ByteArrayOutputStream( );
            int c;
            while ( (c = fis.read()) != -1) {
                bos.write( c );
            }
            pictureIndex = wb.addPicture( bos.toByteArray(), HSSFWorkbook.PICTURE_TYPE_JPEG  );
        } finally {
            if (fis != null) fis.close();
            if (bos != null) bos.close();
        }
        return pictureIndex;
    }
}
 
 

출처 : http://www.jakartaproject.com/article/jakarta/1126065370768
Posted by 마음소리
쿠키에 대한 자세한 정보는 다음 주소에서 확인하세요.
http://www.ietf.org/rfc/rfc2109.txt

  • 쿠키는 서버가 브라우저에 전송하는 정보입니다.
  • 이후 서버에 접속할 때마다 서버로 쿠키를 전송합니다.
  • 제약사항
    • 브라우저는 사이트당 20개의 쿠키만 받아들임
    • 사용자당 총300개만을 사용할 수 있음
    • 각 쿠키의 크기는 4KB
    • 쿠키를 받아들이지 않는 브라우저가 있음

샘플 코드 (Language : java)
  1. /*
  2. 서블릿 post 메소드
  3. */
  4. public void doGet (HttpServletRequest request, HttpServletResponse response) throws  ServletException, IOException
  5. {
  6.    // 쿠키 읽기
  7.    Cookie[] cookies = request.getCookies();
  8.  
  9.    /*
  10.    쿠키 정보를 사용하는 코드
  11.    */
  12.  
  13.    // 쿠키 만들기
  14.    Cookie cookie = new Cookie(name, value);
  15.  
  16.    // 쿠키 쓰기
  17.    response.addCookie(cookie);
  18. }

Posted by 마음소리

복사 http://blog.naver.com/ceresrits/40047089002

이 포스트를 보낸곳 ()

스트럿츠에서 중복처리를 위해 사용하는 saveToken() 에대해서 간략히 알아보자

원리는 간단한듯하면서 복잡미묘? ㅋㅋ

요청(request)에 값을 실어서 그값을 검사해 같은값이 들어오는지 다른값이 들어오는지에 따라 중복여부를 처리한다.

코드로 볼까나?


아 우선 순서를 알아보자 흔히 MVC2 Parttern에서 중복을 처리할때 스트러츠 프레임 워크는

두번의 액션(control)을 거친다.


request         firstAction                      viewpage(jsp)                                         두번째액션

(요청)             (여기서 값                      두번째 액션으로                                      넘어온값을

-------->         을 심는다) --------->   값을 전달하기위해 hidden--------------> 체크하여 중복

                                                          값을 넘긴다                                           확인을 한다.


첫번째 액션

public class firstAction()extends DispatchAction{

    public ActionForward first(.......){

          ActionForward forward=null;

              saveToken(request);//요문장이 요청에 값을 담는문장이다. 그대로 사용하면 된다.

              forward=mapping.findForward(String name);//뷰페이지로 이동하기위한 포워드를 설정

            return forward;

     }

}


view page(jsp)

일반폼의 경웨는 폼안에 히든값을 심어서 두번째 페이지로 토큰을 전달한다. 이것도 이형식 그대로

ctrl+c  ctrl+v해서 쓰자

<form ...action="second.do?method=second" > //액션의 맵핑은 *.do라고 가정하자

<input type=hidden name="org.apache.struts.taglib.html.TOKEN" value="<%=session.
getAttribute("org.apache.struts.action.TOKEN")%>" >
  .

  .

  .

</form>


두번째 액션

public class SecondAction() extedns DispatchAction{

    public ActionForwad Second(....){

           ActionForward forward=null;

              if(!isTokenValid(request)){
                   forward=mapping.findForward("re_list");//값이 중복이므로 포워드한다.
              }else{
                            resetToken(request);

                                  .

                                  .//중복이 아니면 작업내역
                             forward=mapping.findForward("re_list");//작업을 마치고 포워드 설정
                             saveToken(request);//새로운 값을 심어 다음의 과정을 준비한다.(f5 key예방)


            return forward;

       }


}


이상으로 saveToken()을 이용한 중복명령 방지에 대해서 알아보았다.



 

Posted by 마음소리
★ 자바 네이티브 인터페이스를 이용한 C/C++ 코드와 결합

보통 서로 다른 장, 단점을 갖고 있는 JAVA와 C++, 이 두 언어를 결합할 수만 있으면 큰 시너지를 발휘할 수 있다고 생각한다. 뭐, 어느 정도의 범위에서는 그렇긴 하지만...그러나 왠만하면, 정말 어쩔 수 없는 경우를 제외하고는 이 JNI 기술은 활용하지 않는 것이 최상이라고 난 생각한다. -_-;;

이 제 마소에서 두 언어를 결합하는 방법을 전격 공개한다. 먼저 자바의 JNI를 이용해 C로 작성된 Win32 DLL을 호출하는 방법을 알아볼 텐데, 자바 코드에서 만든 데이터를 C 코드로 전달하거나 혹은 C 코드에서 생성된 데이터를 자바로 전달하는 방법, 한글 문제를 해결하는 방법 등도 포함돼 있다. 더불어 C/C++에서 자바 VM을 이용하는 방법도 함께 소개한다.

자바 네이티브 메쏘드(Java Native method, 이하 JNI)는 다른 언어로 작성된 코드를 자바에서 호출하도록 만들어진 규약이다. 현재는 C/C++에 대한 호출만을 정확하게 지원한다. 어떻게 보면 JNI는 자바가 만들어진 철학과 정반대되는 것이다. 네이티브 함수가 플랫폼에 종속적이기 때문이다. 자바의 가장 큰 장점 중의 하나로 내세우는 ‘플랫폼 독립적’이라는 부분을 해치는 JNI 규약이 만들어진 것은, 자바의 현실적인 문제들 때문이다.

먼저 속도 문제를 들 수 있다. JIT 컴파일러와 같은 기술로 자바 프로그램의 속도가 예전보다는 많이 빨라지고 있지만, 자바는 원칙적으로 바이트 코드를 인터프리트해 수행되기 때문에 아무리 자바 기술이 발전한다 해도, 네이티브 코드(플랫폼에 종속적인 기계어 코드)의 속도를 따라갈 수는 없다. 사실 자바의 처리 속도 문제는 자바가 안고 있는 가장 큰 단점이기도 하다. 따라서 아주 빠른 처리가 요구되는 대량의 계산 작업이나 실시간(real-time) 처리에는 자바를 이용하기 힘들다. - 이제 속도 문제는 신경을 안 써도 된다. 정말 속도가 중요시되는 전문 과학 분야를 제외하고는 JAVA의 처리 속도로도 거의 모든 문제를 해결할 수 있다.

JNI 가 필요한 것은 단지 속도 문제만이 아니다. 기존에 작성된 프로그램이나 기존의 시스템(legacy)과의 연계 문제가 있으며, 더욱 중요한 이유는 플랫폼에 따라 다르게 제공되는 서비스를 이용할 수 있다는 점이다. 자바의 클래스 라이브러리는 방대하고 다양한 서비스를 제공하지만, 특정 플랫폼에서 제공하는 고유의 서비스의 기능을 모두 포함할 수는 없다. 특히, 특수한 목적으로 제작된 하드웨어를 자바에서 제어해야 할 필요가 있다고 한다면, 자바만으로 해결하기는 힘들다.

이 때문에 JNI가 필요하다. 지나치게 JNI에 의존하는 것은 자바가 갖고 있는 많은 장점들을 해치는 결과를 초래할 수 있지만, 특정 부분에 적절하게 사용한다면 JNI는 자바의 장점과 C/C++의 장점을 골고루 이용할 수 있는 길을 제공한다.

여기서 한 가지 궁금증이 생길 것이다. 그렇다면 과연 JNI를 어떤 부분에 사용해야 앞서 언급한 효과를 볼 수 있을까? JNI가 사용되면 좋은 곳을 정리해 보면 다음과 같다.

1. 속도 문제가 있는 계산 루틴
2. 자바에서 하드웨어 제어
3. 자바에서 지원되지 않은 특정 운영체제 서비스
4. 기존의 프로그램에서 자바가 제공하는 서비스를 이용

자 바가 엔터프라이즈 솔루션에 많이 사용되는 것은 자바가 갖고 있는 많은 장점들이 대규모 시스템 구축에 적합하다는 강점을 인정받고 있는 셈이다. JNI는 그 자체가 중요한 솔루션이 될 수는 없지만, 기존의 코드나 기존의 시스템 혹은 이질적인 다른 시스템과의 인터페이스에 사용될 수 있는, 자바가 가진 좋은 기능 중의 하나이다.


★ 간단한 JNI 프로그래밍, HelloWorld

자바 프로그래밍을 한 번이라도 해 본 사람이라면, 다음과 같은 HelloWorld 프로그램을 작성해 보았을 것이다.

public class HelloWorld {

       public static void main(String arg[]) {

             System.out.println("Hello, World");

       }

}


이제 이 프로그램을 JNI 버전으로 다시 만들어 보는 것부터 시작해 보려 한다. 가장 간단한 JNI 호출 방법과 컴파일 방법을 익히기 위해서이다

1단계 : 네이티브 메쏘드를 가진 클래스 작성

두 개의 자바 프로그램을 작성하고, 컴파일해 NativeHello.class, HelloWorld.class 파일을 생성한다.

C:\jni>javac NativeHello.java
C:\jni>javac HelloWorld.java

NativeHello.java 프로그램은 C/C++로 작성된 Win32 네이티브 코드를 호출하기 위한 네이티브 메쏘드를 선언하는 부분이다.

public native void SayHelloWorld();

SayHelloWorld 는 자바에서 호출하는 메쏘드 이름이 되는데, ‘native’ 키워드를 이용해 선언하며, 메쏘드의 몸체(body)는 없다. 몸체는 C/C++ 코드로 컴파일된 DLL에 만들어진다. HelloWorld.Java는 NativeHello 객체를 만들어 SayHello World() 메쏘드를 호출하는 간단한 형태의 프로그램이다

2단계 : C 헤더 파일 생성

javah 명령을 써서 다음과 같이 헤더 파일을 생성한다. javah 명령은 JDK에 포함돼 있는 명령으로 .class 파일을 읽어, 여기에 들어있는 네이티브 메쏘드를 판독하고, 이를 C 헤더 파일로 생성해 낸다.

C:\jni>javah -jni NativeHello

이 명령을 쓰려면 물론 JDK의 환경변수(특히 CLASSPATH 등)가 세팅돼 있어야 한다. 이렇게 하면 NativeHello.h라는 C 언어 헤더 파일이 생성된다. 그리고 옵션으로 jni를 주어야 한다. 물론 jni 옵션을 주지 않아도 헤더 파일이 생성되지만, JNI 스타일의 헤더가 아닐 것이기 때문이다. JDK 1.0 스타일의 헤더 파일을 생성하고자 한다면 -stubs 옵션을 준다.

생성된 헤더 파일인 NativeHello.h를 살펴보자. 이 파일은 다음과 같은 함수 선언을 포함하고 있다.

JNIEXPORT void JNICALL Java_NativeHello_SayHelloWorld(JNIEnv *, jobject);

이 파일의 첫 부분에 ‘DO NOT EDIT THIS FILE’이란 말이 있다. 실제로 이 헤더 파일은 수정하지 말아야 한다. 이 Java_Native Hello_SayHelloWorld 함수 선언은 JNIEnv struct 포인터와 jobject 등 두 개의 인수를 갖고 있다. 이 두 개의 인수는 모든 JNI 호출에 추가된다.

첫번째 인수인 JNIEnv 포인터는 자바 가상머신(Virtual Machine, 이하 VM) 영역에 대한 포
인터이고, 두번째 jobject는 C++의 this에 해당한다. 즉, 클래스를 참조하는데 사용된다.

생성된 헤더 파일에서 ‘Signature:’ 부분도 눈여겨볼 필요가 있다. 현재 ‘V’라는 글자가 있는데 이것은 리턴 타입이 void란 의미이다.

javah 사용과 관련해 지정할 수 있는 옵션들은 다음과 같은 것들이 있다.

-o : 출력될 파일의 이름을 지정한다.
-d : 출력 디렉토리를 지정한다.
-jni : JNI 스타일의 헤더를 생성하도록 한다.
-td : 작업용 임시 디렉토리를 지정한다.
-stubs : stub 파일을 생성한다(JDK 1.0 스타일).
-trace : stub 파일에 추적 정보를 덧붙인다.
-verbose : 작업에 대한 자세한 정보를 출력한다.
-classpath : 클래스 경로를 지정한다. 디렉토리는 ‘;’으로 분리한다.
-version : 빌드 버전을 출력한다.

3단계 : 네이티브 메쏘드의 몸체가 될 C 코드 작성

1. 비주얼 C++에서 Win32 Dynamic-Link Library 타입으로 Native Hello라는 이름의 프로젝트를 생성한다.
2. 위저드의 1/1 단계에서 ‘A Simple Projcet’를 선택한다.
3. javah로 생성된 헤더 파일(NativeHello.h)을 프로젝트 디렉토리로 옮기고, NativeHello
.cpp 파일을 <리스트 4>와 같이 수정한다. 여기서는 헤더에서 생성된 함수의 이름을 그대로
구현하는 것이다.
4. Prject / Settings 메뉴를 선택한 뒤, <화면 1>과 같이 다음 인클루드 디렉토리를 추가
한다. 이 jni.h 파일은 네이티브 메쏘드를 위해 JDK가 제공하는 헤더 파일이다.

C:\jdk1.2.2\include,C:\jdk1.2.2\include\win32

화면 1

화면 1


4단계 : 빌드하여 NativeHello.dll 파일을 생성하고 프로그램을 수행한다.

NativeHello.dll 파일을 NativeHello.class 파일이 있는 곳에 복사한다. 이렇게 하면 준비가 다 된 것이다. 이제 HelloWorld Java를 다음과 같이 수행한다.

C:\jni>java HelloWorld

< 화면 2>는 이것의 수행된 결과이다. 간단한 메시지 박스가 나타난다. HelloWorld.java에서 NativeHello 클래스의 SayHelloWorld() 네이티브 메쏘드를 호출하는데, 이 네이티브 메쏘드의 몸체가 바로 NativeHello.cpp 파일의 Java_NativeHello_ SayHelloWorld() 함수가 되는 것이다.

이 함수는 간단히 MessageBox()란 Win32 API를 호출하는 내용으로 돼있다. DLL은 사실상 Win32의 실행 파일과 마찬가지로 모든 Win32 API를 사용할 수 있기 때문에 이론적으로는 JNI에서 모든 Win32 API를 이용할 수 있다.

화면 2

화면 2



★ 자바의 데이터 타입, C/C++의 데이터 타입

우 리는 앞에서 JNI 프로그래밍의 전체적인 과정을 훑어보기 위해 가장 간단한 형태의 JNI 프로그램을 작성해 보았다. 사실 독자들이 가장 궁금해 할 내용은 아마도 자바 코드에서 만들어진 데이터를 C 코드로 전달하거나, 혹은 C 코드에서 생성된 데이터를 자바로 전달해주는 방법일 것이다. 일단 다음과 같이 String 타입의 객체를 인수로 전달하는 경우를 살펴보자.

public class NativeClass {

       public native void setString(String s);

       public native void setInt(int i);

       public native void setData(float f, double d);

       ...

}


이 코드를 컴파일한 후 javah로 C 헤더 파일을 생성하면 헤더에는 다음과 같은 내용의 함수
선언이 생성된다.

JNIEXPORT void JNICALL Java_NativeClass_setString(JNIEnv *, jobject, jstring);
JNIEXPORT void JNICALL Java_NativeClass_setInt(JNIEnv *, jobject, jint);
JNIEXPORT void JNICALL Java_NativeClass_setData(JNIEnv *, jobject, jfloat, jdouble);

생성된 함수 선언을 보면, 모든 함수의 인수가 두 개 이상인 것을 확인할 수 있다. 처음 두
개의 인수(JNIEnv *, jobject)는 모든 JNI 함수에 생긴다. 세번째 인수부터가 우리가 전달
하고자 하는 인수이다.

JNI에는 C 언어와의 데이터 타입의 차이를 극복하기 위해 헤더에 ‘j’로 시작하는 새로운 타입을 재정의했다. 이 데이터 타입의 크기가 C와 같을 수도 있지만, 경우에 따라서는 다를 수도 있다.

예 를 들어 Win32 환경의 경우 jint는 int와 같은 32비트 정수이다. 하지만 jchar은 char 타입과 다르다. 자바의 char은 16비트 정수이며, C의 char 타입은 8비트 정수이다. 또한 일반적으로 비주얼 C++에서 사용하는 BOOL은 int 타입을 재정의한 것이다. 따라서 자바의 boolean에 해당하는 jboolean 타입과는 크기가 다르다. 참고로 자바에서 true, false에 해당하는 부울 값은 에 다음과 같이 정의돼 있다.

#define JNI_FALSE 0
#define JNI_TRUE 1

이런 차이점에 유의해서 코드를 작성해야 한다(<표 1>).

<표 1> JNI에서 사용하는 데이터 타입과 범위
Java
C(JNI)
비트수
C/C++(Win32)
boolean
jboolean
8 unsigned
unsigned char
byte
jbyte
8
unsigned char
char
jchar
16 unsigned
unsigned short
short
jshort
16
short
int
jint
32
int
long
jlong
64
_int64 (비주얼C++)
float
jfloat
32
float
double
jdouble
64
double
void
void
-
void


★ UTF-8 문자열 다루기

자바 코드와 C 코드 간에는 jstring 타입으로 문자열을 주고받는데, jstring은 객체이기 때문에 C에서 ‘char *’와 같은 형태로 바로 이용할 수는 없다. 때문에 이를 위해 몇 가지 함수들이 제공된다.

우 리는 두 가지 문제만 해결할 수 있으면 충분하다. 하나는 자바 코드에서 넘어온 jstring 타입을 C의 ‘char *’로 바꾸어 처리하는 방법과 C 코드에서 생성한 문자열을 자바로 리턴하는 방법이다. 먼저 자바에서 생성된 String 타입(jstring)의 객체를 C 코드에서 다루는 방법을 살펴보자.

자바에서는 아스키(ASCII) 문자열 이외에 UTF-8, UTF-16과 같은 문자열 포맷이 지원된다. 이것은 7비트로만 나타낼 수 없는 문자들(한글, 한자 등), 2바이트 이상으로 표현되는 문자들을 지원하기 위한 포맷이다. UTF-8(Universal Character Set Transforma tion Format, 8bit)은 8비트 즉, 바이트의 나열로 표시된다.

영문 아스키 문자의 경우는 일반적인 C의 문자열과 다르지 않다. 0x0001∼0x007F 범위의 문자는 1바이트를 사용하며, 0x0080~0x07FF 범위의 문자를 표현할 때는 2바이트가 사용된다.

첫번째 바이트는 이진수 110으로 시작하며, 두번째 바이트는 10으로 시작한다. 따라서 연속
된 두 바이트를 다음과 같이 조합해 하나의 유니코드 문자를 만들게 된다.

((첫번째_바이트 & 0x1f)<<6) + (두번째_바이트 & 0x3f)

이와 비슷하게 0x0800∼0xFFFF 범위 사이의 문자 코드는 3개 바이트를 사용해 표현하며, 다음과 같이 연산해 하나의 유니코드 문자를 만들어 내게 된다. <표 2>는 UTF-8이 인코딩되는
방법을 보여주고 있다.

((첫번째_바이트 & 0xf)<<12)+((두번째_바이트 & 0x3f) <<6) + (세번째_바이트 & 0x3f)

<표 2> UTF-8 포맷의 문자열 인코딩 방식
유니코드(16진수)
인코딩된 바이트 수
UTF-8로 인코딩된 비트(2진수)
0x0001~0x007F
1
0xxxxxxx
0x0080~0x07FF
2
110xxxxx 10xxxxxx
0x0000
-
-
0x0800~0xFFFF
3
1110xxxx 10xxxxxx 10xxxxxx

자 바 VM이 사용하는 UTF-8 포맷에 대해 주의할 점이 있다. 첫번째는 널(null, 0x0000) 문자에 대한 표현이다. 0x0000 문자는 1바이트가 아닌 두 바이트로 표시된다. 즉, 0x0000 문자를 2진수 비트로 표시해 보면,

11000000 10000000

의 두 바이트로 나열된다. 따라서 C에서처럼 ‘\0(2진수 000 00000)’으로 끝나는 문자열이 아니라는 점을 알 수 있다. 만일 여러분이 jstring을 strcpy()와 같은 함수에 직접 사용한다면, ‘\0’ 문자가 없기 때문에 분명 프로그램이 죽을 것이다. 반대로 C의 문자열을 그대로 jstring으로 넘긴다면 자바 VM이 죽는다. jstring을 잘못 사용하는 예를 들어보자.

JNIEXPORT void JNICALL Java_WinDialogBox_setTextField(JNIEnv *env, jobject obj, jstring str) {
    char strBuff[128];
    strcpy(strBuff, str) ; // C 모듈이 crash한다.
}

JNIEXPORT jstring JNICALL Java_WinDialogBox_getTextField(JNIEnv *env, jobject obj) {
    char strBuff[128] = "Hello Java VM";
    return strBuff; // VM이 crash한다.
}

JNI는 jstring과 C의 문자열 처리를 위해 GetStringUTFChars (), ReleaseStringUTFChars() 함수를 제공한다.

GetString UTFChars() 함수는 UTF-8 포맷의 문자열을 널로 끝나는 C 스타일의 문자열로 전환해 포인터를 리턴하는 함수이다.

반 대로 Relea seStringUTFChars() 함수는 원상태로 복구하는 함수이다. 덧붙여 C 스타일의 문자열을 UTF-8 문자열로 만들려면, New StringUTF() 함수를 사용한다. 다음은 C 문자열과 자바의 jstring 사이의 문제를 해결한 C++ 코드이다.

JNIEXPORT void JNICALL Java_WinDialogBox_setTextField(JNIEnv *env, jobject obj, jstring str) {
    char strBuff[128];
    const char *sz = env->GetStringUTFChars(str, 0) ;
    strcpy(strBuff, sz) ;
    env->ReleaseStringUTFChars(str, sz) ;
}

JNIEXPORT jstring JNICALL Java_WinDialogBox_getTextField(JNIEnv *env, jobject obj) {
    char strBuff[128] = "Hello Java VM";
    return env->NewStringUTF(strBuff) ;
}

똑같은 내용이지만 C에서 호출한다면, 즉 확장자가 .c인 프로그램에서는 JNI 함수를 호출하는 방법이 약간 달라진다. 다음은 같은 내용의 C 코드이다.

JNIEXPORT void JNICALL Java_WinDialogBox_setTextField(JNIEnv *env, jobject obj, jstring str) {
    char strBuff[128];
    const char *sz = (*env)->GetStringUTFChars(env, str, 0) ;
    strcpy(strBuff, sz) ;
    (*env)->ReleaseStringUTFChars(env, str, sz) ;
}

JNIEXPORT jstring JNICALL Java_WinDialogBox_getTextField(JNIEnv *env, jobject obj) {
    char strBuff[128] = "Hello Java VM";
    return (*env)->NewStringUTF(env, strBuff) ;
}

이처럼, 같은 내용이라도 JNI 함수를 C와 C++에서 호출하는 방법에 차이가 있음을 알아두자.

(*env)->FindClass(env, “java/lang/String”); // C의 경우
env->FindClass(“java/lang/String”); // C++의 경우

이 내용들을 가지고 간단한 Win32 에디터를 만들어 보자.

먼 저 <리스트 5>와 <리스트 6>에 있는 두 개의 자바 소스 파일을 만들고 각각을 컴파일한다. <리스트 5>의 WinDialgoBox.java 프로그램은 세 개의 네이티브 메쏘드를 선언하고 있는 클래스이며, 실제 이 네이티브 메쏘드의 몸체는 WinDialogBox.DLL 안에 만들어질 것이다. 그리
고 앞과 마찬가지로 자바 헤더 파일을 만든다.

C:\jni>javah -jni WinDialogBox

이 렇게 하면 WinDialogBox.h가 생성된다. 이 파일은 세 개의 네이티브 함수에 대한 선언을 포함하고 있다. 그리고 비주얼 C++에서 ‘WinDialogBox’란 프로젝트를 만든다(‘Win32 Dynamic-Link Library’ 타입으로 생성하며, 위저드의 첫 단계에서 ‘A Simple Projcet’를 체크한다). 그리고 javah 프로그램이 생성한 WinDialogBox.h 파일을 프로젝트에 포함시키고, Win DialogBox.cpp 파일을 <리스트 7>과 같이 편집한다.

이 C++ 프로그램의 내용을 잠깐 살펴보자. Java_WinDialog Box_doModal() 함수는 다이얼로그를 화면에 표시하는 함수이다. Java_WinDialogBox_setTextField()는 자바 코드에서 문자열을 넘겨 전역 문자 배열에 저장하는 함수이며, Java_WinDialogBox _getTextField() 함수는 전역 문자 배열의 내용을 jstring 타입으로 변환해 자바로 문자열을 리턴하는 함수이다.

DialgProc() 함수는 다이얼로그 프로시저이다. 이 다이얼로그 프로시저는 두 개의 메시지(WM_INITDIALOG, WM_COMMAND)를 처리하고 있다. 다이얼로그가 화면에 나타날 때, 즉 WM_INIT DIALOG 메시지가 전달되면 SetDlgItemText() 함수를 이용해 전역 문자열에 있던 문자를 에디트 컨트롤에 지정한다.

반대로, 버튼이 눌리면 GetDlgItemText() 함수를 이용해 편집된 문자열을 얻어와 전역 문자열에 저장한다. 이 저장된 값은 getTextField() 메쏘드를 통해 자바 프로그램으로 리턴되어 편집된 내용을 출력하게 된다.

이 C++ 코드가 생성하는 DLL은 리소스를 가지고 있다. 따라서 프로젝트에 리소스를 추가하고 다이얼로그 박스를 생성한다. 다이얼로그 박스는 <화면 3>과 같이 디자인한다. 이 다이얼로그 박스에는 IDC_EDIT_FIELD란 ID를 갖는 에디트 컨트롤이 포함돼 있다. 프로그램을 수행하면 <화면 4>와 같은 Win32 다이얼로그 박스가 나타난다. 여기에 입력된 내용은 다시 자바 프로그램으로 리턴돼 <화면 5>와 같이 도스창 화면에 출력된다.

화면 5

화면 5


지금까지 기본적인 데이터 타입인 jint, jstring 등의 데이터를 자바와 C 코드에서 넘겨주고 받는 방법들을 살펴보았다.

참 고로 다음 코드는 자바에서 Win32 애플리케이션이 프로그램의 상태와 관련된 정보를 저장하기 위해 사용하는 Win32 API 함수 중의 하나인 GetPrivateProfileString()을 구현해본 것이다(레지스트리의 등장으로 요즘은 거의 사용하지 않지만). JNI는 이렇듯, 운영체제의 특징적인 서비스를 이용할 수 있는 장점이 있다.

jstring JNICALL Java_Win32Profile_GetPrivateProfileString(JNIEnv *jni, jclass, jstring section, jstring key, jstring defaut, jint maxLen, jstring name) {
    char returned[4096];
    LPCSTR file;
    LPCSTR szSection;
    LPCSTR szKey;
    LPCSTR szDefault;
    jint ret = 0;
    file = jni->GetStringUTFChars(name, 0);
    szSection = jni->GetStringUTFChars(section, 0);
    szKey =jni->GetStringUTFChars(key, 0);
    szDefault=jni->GetStringUTFChars(defaut, 0);

    if (0 != strcmp(file, “win.ini”))
        ret = GetPrivateProfileString(szSection, szKey, szDefault, returned, maxLen, file);
    else
        ret = GetProfileString(szSection, szKey, szDefault, returned, maxLen);
    if (*returned == ‘\0’)
        strcpy(returned, szDefault);
    jni->ReleaseStringUTFChars(name, file);
    jni->ReleaseStringUTFChars(section, szSection);
    jni->ReleaseStringUTFChars(key, szKey);
    jni->ReleaseStringUTFChars(defaut, szDefault);
    return jni->NewStringUTF(returned);
}

주의 깊은 독자라면 이 프로그램을 테스트하면서 커다란 문제점을 발견할 수 있을 것이다. 그것은 바로 개발자들을 끊임없이 괴롭혀온 문제 중의 하나인 한글 문제이다.

자 바에서 한글이 포함된 문자열을 C로 보내면(UTF-8 포맷으로), C에서는 이를 직접 사용할 수 없다. 또한 다이얼로그 박스에서 한글이 입력돼 자바로 전달하면 이것도 나타나지 않거나 ‘???’로 표시된다. 이런 문제는 왜 생기며, 어떻게 해결할 수 있을까?

화면 3

화면 3


화면 4

화면 4



★ JNI와 한글 문제

자바는 기본적으로 모든 문자열을 유니코드(Unicode)로 처리한다. 유니코드는 한 글자를 16비트로 표현하는 표준 코드로 바이트 문자열에 비해 크기가 커지는 단점이 있지만, 다양한 언어를 자유롭게 지원할 수 있다는 장점이 있다.

- 자바 : 유니코드(Unicode)
- JNI : UTF-8
- C/C++ : 바이트 문자열(KSC5601)

하 지만 보통 Win32, C/C++ 환경에서 사용하는 문자열은 유니코드가 아닌 바이트 문자열(KSC5601)이다. 더구나 JNI에서 사용하는 jstring은 UTF-8 포맷으로 인코딩된 문자열이므로 직접 C 코드에서 사용하는 데는 어려움이 있다. 앞의 프로그램에서 해본 대로 영문자의 경우는 아무런 문제가 없다.

UTF-8 포맷이 영문자에 대해서는 1바이트로 인코딩되기 때문에 바이트 문자열과 UTF-8 포맷의 문자열은 사실상 같은 것이라고 보아도 무방하다. 하지만 한글의 경우는 다르다. 예를 들어보자. ‘한’이란 문자가 있다 하자. 이 글자는 <표 3>에서 볼 수 있듯 전혀 다른 형태로 저장된다.

만일 JNI를 통해 한글을 쓰고 싶다면, JNI를 통해 넘어온 jstring 문자열은 UTF-8 포맷이므로 이를 KSC5601 코드로 바꾸는 작업이 필요하다. 이 작업을 C 프로그램에서 해야 한다면, 사실 배보다 배꼽이 더 큰 일이다. 더욱이 UTF-8은 KSC5601 코드를 변환한 것이 아니라 유니코드를 인코딩한 것이므로 일은 더욱 복잡해진다.

자 바에는 이 코드 변환에 대한 해결책이 이미 준비돼 있다. 따라서 이것을 C에서 해결하려 하기보다는 C에서는 단순하게 바이트 문자열만을 다루고, 유니코드와 UTF-8은 자바 코드에서 다루도록 하는 것이 프로그래밍을 훨씬 간단하게 한다.

즉, jstirng을 사용하지 않고, 바이트 배열을 주고받는 것이다. 경험으로 미루어 볼 때 자바와 C 간의 데이터 교환에서 가장 중요한 것은 사실 jstring보다 바이트 배열이다. 바이트 배열은 임의의 데이터 형태를 모두 전달할 수 있기 때문이다. 이것은 비단 JNI에만 국한된 문제는 아니다.

서블릿(sublet), 자바 애플리케이션(application), 애플릿(applet) 모두 외부에서 입력된 문자열을 처리해야 할 필요가 있기 때문에 자바 프로그래머라면 언제고 한번쯤은 부딪히게 되는 문제이다.

자 바에서 외부에서 입력된 바이트 문자열을 자바 내부에서 사용하는 유니코드 혹은 UTF-8로 변환하는 것은 의외로 간단하다. <리스트 8>은 표준 입력에서 바이트 문자열을 입력받아 내부적으로 사용되는 String 객체로 다시 생성하는 방법을 보이고 있다.

System.in.read()를 통해 바이트 단위로 입력된 바이트 배열은 String 생성자를 통해 유니코드로 전환된다. String 생성자를 이용할 때, 바이트 배열이 인코딩된 방식을 지정해줄 수 있다.

이 생성자가 생성해낸 String 객체는 유니코드 문자열을 가지게 된다. 이 내용을 출력해 보면 똑같은 내용이 출력된다(<화면 6>). 자바에서 표준 출력에 출력할 때, 플랫폼 환경에 맞추어 다시 인코딩한 후 출력하기 때문이다.

화면 6

화면 6


String(byte[] bytes, String enc); // 바이트 문자열을 유니코드 문자열로 전환
byte[]getBytes(String enc); // 유니코드 문자열을 바이트 문자열로 전환

이 두 개의 메쏘드는 String 클래스의 메쏘드로, 코드 변환에 유용하게 사용된다. enc에는 인코딩된 방식을 써주면 된다. 예를 들어 Win32 환경에서 가장 많이 사용하는 KSC5601 코드의 경우, 바이트 문자열을 유니코드로 전환하려면 다음과 같이 한다.

byte buffer [] = new byte[200];
...
String uni = new String(buffer, KSC5601);

또한 유니코드를 KSC5601 바이트 문자열로 바꾸고자 한다면, 다음과 같이 해주면 된다.

byte[] buffer;
String uni = “한글”;
buffer = uni.getBytes(KSC5601);

앞 에서 작성해 본 WinDialogBox.java 프로그램을 한글 처리를 염두에 두고 다시 작성해 보자. 파일 한글 처리가 문제되는 것은 외부에서 입력받는 내용이 유니코드 문자열이 아니라, 일반적으로 KSC5601 코드의 바이트 문자열이라는 점에 있다. 따라서 앞서 이야기한 KSC5601 바이트 코드와 유니코드 간의 변환을 이용하되, 자바 코드 외부에서 입력 혹은 전달되는 내용은 모두 바이트 배열로 처리하는 것이 바람직하다.

JNI에서는 <표 1>의 JNI에서 볼 수 있는 데이터 타입 이외에 다음과 같은 데이터 타입을 지
원한다.

struct _jobject;
typedef struct _jobject *jobject;
typedef jobject jclass;
typedef jobject jthrowable;
typedef jobject jstring;
typedef jobject jarray;
typedef jarray jbooleanArray;
typedef jarray jbyteArray;
typedef jarray jcharArray;
typedef jarray jshortArray;
typedef jarray jintArray;
typedef jarray jlongArray;
typedef jarray jfloatArray;
typedef jarray jdoubleArray;
typedef jarray jobjectArray;

객체나 각 기본 타입의 배열들에 대한 지원도 하고 있다. 여기서는 jbyteArray 타입을 이용할 것이다.

<표 3> ‘한’에 대한 코드
바이트 문자열(KSC5601)
유니코드
UTF-8
0xC7 0xD1
0xD55C
0xED 0x95 0x9C
2바이트
16비트 코드
3바이트


★ JNI에서 자바 배열 객체

자바의 배열은 자바의 객체이다. JNI를 통해 넘겨받은 자바의 배열을 다루기 위해 JNI가 제공하는 함수들에 대해 알아보자.

모든 자바 배열은 크기 필드를 갖고 있다. 다음 함수는 자바 배열 요소 개수를 알아내는 함
수이다.

jsize GetArrayLength(JNIEnv *env, jarray array);

배열의 각각 요소들을 다루기 위해서는 다음 JNI 함수가 필요하다.

jobject GetObjectArrayElement(JNIEnv *env, jarray array, jsize index);
void SetObjectArrayElement(JNIEnv *env, jarray array, jsize index, jobject value);

이 름에서도 알 수 있듯이 이 두 개의 함수는 배열의 요소를 얻거나 요소를 할당하는 함수이다. 배열에서 요소 값을 읽어올 때는 다음과 같은 GetArrayElements() 함수를 이용한다. 이 함수의 세번째 인자 isCopy는 자바 VM에서 배열의 요소들이 기억장소에 연속적으로 배치되어 있는지의 여부를 jboolean값으로 리턴한다. 이 값이 참(JNI_ TRUE)이라면 연속적으로 배
열된 복사본이라는 사실을 알 수 있고, 거짓(JNI_FALSE)이라면 실제 배열 요소를 다루어야
한다.

NativeType GetArrayElements(JNIEnv *env, jarray array, jboolean *isCopy);

< 표 4>는 GetArrayElements() 함수의 실제 함수 이름과 타입별 리턴 타입, 그리고 그에 해당하는 자바의 배열 타입을 정리한 것이다. 이 함수를 사용하고 나면, ReleaseArrayElements() 함수를 사용해 복사본을 기억장소에서 해지해야 한다. 이 함수의 실제 함수 이름과 리턴 타입도 <표 5>에 정리해 놓았다.

<표 4> 배열을 다루기 위한 JNI의 GetArrayElements 함수
JNI 함수
리턴타입
자바 배열 타입
GetBooleanArrayElements()
jboolean *
boolean[]
GetByteArrayElements()
jbyte *
byte[]
GetCharArrayElements()
jchar *
char[]
GetShortArrayElements()
jshort *
short[]
GetIntArrayElements()
jint *
int[]
GetLongArrayElements()
jlong *
long[]
GetFloatArrayElements()
jfloat *
float[]
GetDoubleArrayElements()
jdouble *
double[]

<표 5> 배열을 다루기 위한 JNI의 ReleaseArrayElements 함수
JNI 함수
네이티브 배열 타입
자바 배열 타입
ReleaseBooleanArrayElements()
jboolean *
boolean[]
ReleaseByteArrayElements()
jbyte *
byte[]
ReleaseCharArrayElements()
jchar *
char[]
ReleaseShortArrayElements()
jshort *
short[]
ReleaseIntArrayElements()
jint *
int[]
ReleaseLongArrayElements()
jlong *
long[]
ReleaseFloatArrayElements()
jfloat *
float[]
ReleaseDoubleArrayElements()
jdouble *
double[]

void ReleaseArrayElements(JNIEnv *env, jarray array, NativeType elems, jint mode);

이 함수는 mode에 세 가지 다른 값을 지정할 수 있다.

0 : 변경된 내용을 반영하고, 복사본의 기억장소를 해지한다.
JNI_COMMIT : 변경된 내용을 반영만 하고, 복사본의 기억장소를 해지하지 않는다.
JNI_ABORT : 변경된 내용을 반영하지 않고, 복사본의 기억장소를 해지한다.

이 제 WinDialogBox의 두번째 버전(WinDialogBox2)을 만들어 보자. 이 프로그램을 만드는 방법은 앞서 보았던 WinDialogBox 프로그램을 만드는 방법과 꼭 같다. 자바 소스 프로그램을 <리스트 9>, <리스트 10>과 같이 만들고 컴파일한다. 그 다음, javah 명령을 이용해 WinDialogBox2.h 파일을 생성한다.

C:\jni> javac TestWinDialogBox2.java
C:\jni> javac WinDialgoBox2.java
C:\jni> javah jni WinDialgoBox2

이 제 DLL을 생성하기 위한 프로젝트로 WinDialogBox2를 새롭게 만든다. 여기에 다이얼로그 박스를 만들고, WinDialogBox2.cpp 프로그램을 <리스트 11>과 같이 작성한다. 빌드하여 만들어진 WinDialgoBox2.DLL 파일을 클래스 파일이 있는 곳에 복사하고, 다음과 같이 자바 프로그램을 수행시킨다.

C:\jni> java TestWinDialogBox2

<화면 7>은 TestWinDialogBox2 프로그램을 수행시킨 화면이다. 이제 한글 문제가 완전히 해
결됐다.

화면 7

화면 7


이처럼 JNI를 이용해 C 프로그램과 데이터 교환을 할 때 가장 유용하게 사용할 수 있는 것이 바이트 배열이다.


★ C/C++에서 자바 사용

지 금까지 우리는 자바에서 C/C++ 코드를 사용하는 방법을 살펴보았다. 그렇다면 반대로 C/C++에서 VM을 이용하려면 어떻게 해야 할까? 이 부분은 자바에서 C 코드를 호출하는 것에 비해 상대적으로 중요하지 않기 때문에 자세하게 다루지는 않지만, 독자 여러분이 찾아볼 수 있을 정도의 간략한 개요를 정리해 보겠다.

C/C++에서 VM을 사용하기 위해 가장 먼저 해야 할 것은 C 코드에서 자바 VM을 생성하는 일이다. JNI는 C/C++ 애플리케이션에 자바 VM을 포함(embedding)할 수 있도록 지원한다. C/ C++ 애플리케이션에 VM을 생성하면, VM의 모든 클래스와 메쏘드를 이용할 수 있다.

환경에 대한 디폴트 설정 값을 얻어오기 위해 JNI_GetDefaultJavaVMInitArgs() 함수를 사용한다(JDK 1.1 이하). 이 함수는 JDK1_1InitArgs 구조체 포인터를 인자로 받는데, 이 구조체에는 다음과 같이 버전(vm_args.version) 값을 지정해 주어야 한다.

JDK1_1InitArgs vm_args;
vm_args.version = 0x00010001;
JNI_GetDefaultJavaVMInitArgs (&vm_args);

버전 값은 다음과 같이 정의돼 있다(JDK 1.2 이상). 버전으로 사용된 값 0x00010001의 의미는 1.1.2 이상되는 버전의 VM이면, 애플리케이션에서 사용할 수 있다는 의미가 된다.

#define JNI_VERSION_1_1 0x00010001
#define JNI_VERSION_1_2 0x00010002

그런데 이 JNI_GetDefaultJavaVMInitArgs() 함수는 JDK 1.2 이상에서는 쓰이지 않는다. C/C++에서 VM을 사용하려면 먼저 JNI_CreateJavaVM() 함수를 통해 VM을 생성한다.

JavaVM *jvm;
JNIEnv *env;
jint rc = JNI_CreateJavaVM (&jvm, &env, &vm_args);

이 렇게 해 JVM이 생성되면, JNI 함수 중 FindClass()와 Call StaticVoidMethod()를 이용해 자바 클래스와 메쏘드를 호출한다. JVM 사용이 끝나면, DestroyJavaVM() 함수를 써서 VM을 삭제한다.

jvm-> DestroyJavaVM ()

C/C++에서 대략 이런 과정을 통해 VM을 사용하게 된다. C/C++에서 자바 클래스를 이용하는 것이 실제로 많이 요구되지는 않는다.

지금까지 자바에서 C/C++ 코드를 호출하는데 필요한 내용들을 살펴보았다. 특히, 유니코드 관련 내용과 바이트 배열 처리 부분은 상당히 유용한 코드가 될 것이다.

이 JNI 코드 작성을 통해 우리는 몇 가지 사실을 알 수 있다. 우선, 자바에서 모든 Win32 호출을 이용할 수 있다는 점이다. 이것은 유닉스에서도 마찬가지이다. 모든 절차는 똑같으며, 단 유닉스에서는 DLL 대신 공유 라이브러리(shared library)를 사용한다는 점이 다르다.

이 글은 애플릿에서의 문제점은 다루고 있지는 않다(애플릿에서 JNI는 보안과 인증 문제를 갖고 있기 때문에 이 글에서 모두 다루기는 힘들다). C/C++로 액티브X 컴포넌트를 개발하는 프로그래머들이 비주얼 베이직 개발자들을 지원하듯, JNI는 C/C++ 개발자들이 자바 개발자들을 지원하는 창구 역할을 한다.

아무튼 자바에서 기존의 C/C++로 작성된 코드를 사용할 수 있고, 자바와 C/C++ 간의 데이터 교환이 가능하며, Win32 API를 모두 이용할 수 있다는 점은 JNI의 큰 매력이 아닐 수 없다.
Posted by 마음소리


티스토리 툴바