IBM®
메인 컨텐츠로 가기
    Korea [국가변경]    이용약관
 
 
   
        제품    서비스 & 솔루션    고객지원 & 다운로드    회원 서비스    
메인 컨텐츠로 가기

한국 developerWorks  >  WebSphere | 자바  >

WebSphere sMash에서 Java와 PHP 통합하기

developerWorks
문서 옵션

JavaScript가 필요한 문서 옵션은 디스플레이되지 않습니다.

영어원문

영어원문


제안 및 의견
피드백

난이도 : 중급

Ant Phillips, Software Developer, IBM
Zoe Slattery, Software Engineer, IBM

원문 게재일 : 2008 년 9 월 24 일
번역 게재일 : 2009 년 10 월 13 일

WebSphere® sMash 환경에서는 PHP 등의 유명한 웹 기술을 기반으로 하는 대화식 웹 애플리케이션을 빠르게 개발할 수 있으며 PHP 스크립트를 통해 기존 Java™ 자산을 재사용할 수도 있습니다. 이 기사에서는 Java Bridge를 소개한 후 PHP에서 Java 클래스에 액세스하는 방법을 보여 줍니다.

시작하기 전에

먼저 Project Zero 웹 사이트를 자세히 살펴보자. Project Zero 커뮤니티에 가입하여 프로젝트에 동참하거나 토론 포럼에 참여하여 각 개발 단계에서 프로젝트에 대한 의견을 제안할 수 있다. 이 기사에서는 사용자의 시스템에 적합한 JDK(Java Development Kit)가 설치되어 있는 것으로 간주한다. 또한 PHP 개념에도 익숙해야 한다.

Project Zero, WebSphere sMash 및 PHP 시작하기에서는 WebSphere sMash를 다운로드하고 PHP 애플리케이션을 작성하는 방법에 대해 설명하고 있으므로 읽어보기 바란다. 이 기사에서는 WebSphere sMash의 작업 버전과 PHP를 사용한다고 가정하며 이러한 도구는 기사의 "애플리케이션 실행하기" 섹션까지의 단계를 수행하는 데 필요하다.

편집자 주: IBM® WebSphere sMash 및 IBM WebSphere sMash Developer Edition의 토대는 좋은 평판을 받고 있는 Project Zero 인큐베이터 프로젝트이다. Project Zero는 WebSphere sMash를 위한 개발 커뮤니티로 개발자에게 애플리케이션 개발을 위한 무료 플랫폼을 제공하는 동시에 최신 빌드, 최신 기능 및 커뮤니티 지원도 지속적으로 제공할 것이다.




위로


소개

이 기사에서는 Java Bridge를 사용하여 PHP에서 Java 클래스에 액세스하는 방법을 보여 주고 Java 메소드를 호출하고 인스턴스 및 정적 필드에 액세스하는 방법에 대해 설명한다. 또한 예외 처리와 PHP 및 Java 환경 간의 형식 변환에 대해서도 살펴본다.




위로


ZSL, WebSphere sMash 및 Apache Lucene

실제 환경에서 활용할 수 있는 예제를 제공하기 위해 이 기사에서는 Apache Lucene을 사용하여 파일을 인덱싱 및 검색할 수 있는 간단한 검색 엔진을 PHP로 작성하는 과정을 단계별로 설명한다. Apache Lucene은 전체 기능을 제공하는 고성능 텍스트 검색 엔진 라이브러리로 모든 코드가 Java로 작성되었으며 전체 텍스트 검색이 필요한 여러 애플리케이션에 적합한 기술이다.

ZSL에서는 개발자 간의 정보 공유를 향상시키기 위해 Apache Lucene을 사용하여 WebSphere sMash 애플리케이션을 작성했다. 이 문제를 해결하기 위해 소스 코드 및 문서 라이브러리(PDF, PowerPoint, Word, Excel 등)를 인덱싱하는 매시업을 애플리케이션에 통합했다. 결과적으로 이 애플리케이션을 통해 회사 내의 코드에 빠르고 쉽게 액세스할 수 있게 되었다.




위로


WebSphere sMash에서 애플리케이션 작성하기

첫 번째 단계로 다음과 같이 Eclipse에서 새 프로젝트를 작성한다.

  1. File -> New -> Project...를 선택한 후 대화 상자에서 Zero 범주를 펼친다.
  2. 그림 1과 같이 WebSphere sMash PHP Application을 선택하고 Next를 클릭한다.
  3. 프로젝트에 이름(예: MyJavaProject)을 지정하고 Finish를 클릭한다. 그러면 프로젝트가 작성된다.

    그림 1. 새 WebSphere sMash 프로젝트 작성 대화 상자





위로


Java 오브젝트 작성 및 호출하기

이제 Java 오브젝트를 작성하고 호출하는 PHP 스크립트를 작성한다.

  1. public 폴더를 마우스 오른쪽 단추로 클릭하고 New -> File을 선택한다.
  2. 파일에 이름(예: Java.php)을 지정하고 Finish를 클릭한다.
  3. 다음 코드를 파일에 추가한다.
    <?php
        $file = new Java("java.io.File", __FILE__, FALSE);
        var_dump($file);
        var_dump($file->isDirectory());
    ?>
    

  4. Eclipse에서 프로젝트 이름을 마우스 오른쪽 단추로 클릭하고 Run As > WebSphere sMash Application을 선택하여 샘플 코드를 실행한다.
  5. 웹 서버가 로컬 호스트의 8080 포트에서 시작된다.
  6. 이제 브라우저에서 http://localhost:8080/Java.php를 실행하면 그림 2와 같은 출력이 표시된다.

    그림 2. Java 오브젝트를 호출한 웹 브라우저 출력


이 샘플 코드에서는 내장 Java 클래스를 사용하는 PHP 스크립트를 보여 준다. 이 Java 클래스는 Java 클래스의 인스턴스를 작성한 후 스크립트의 인수를 전달하여 가장 일치하는 생성자를 호출한다. 이 예제에서 루트 디렉토리는 "/"FALSE이다. 이 스크립트에서는 루트 디렉토리를 $file이라는 PHP 변수에 저장한다. 그런 다음 일반적인 PHP 오브젝트와 동일한 방식으로 오브젝트에 대한 메소드를 호출하며, 이 예제의 경우에는 isDirectory 메소드를 호출한다.

이 강력한 기능을 사용하면 PHP 스크립트에서 Java 클래스에 쉽게 액세스할 수 있다. 한 가지 주의할 점은 Java 클래스가 애플리케이션 클래스 경로에 있어야 한다는 것이다. 이 예제의 경우 java.io.File은 핵심 Java 클래스 라이브러리에 속해 있기 때문에 항상 사용할 수 있다.




위로


Java 컬렉션 클래스 사용하기

Java에는 맵, 세트, 목록 및 큐를 포함한 다양한 컬렉션 클래스 세트가 있다. 이 샘플 코드에서는 PHP 스크립트에서 이러한 클래스를 활용하는 방법을 보여 준다. 앞에서와 같이 새 PHP 스크립트(예: MoreJava.php)를 작성하고 다음 코드를 추가한다.

<?php
    $map = new Java("java.util.HashMap");
    
    $map->put("title", "Java Bridge!");
    $array = array(1, 2, 3, 4, 5);
    $map->put("stuff", $array);
    var_dump($map->get("stuff"));
    echo $map->get("title");
?>

이제 브라우저에서 http://localhost:8080/MoreJava.php를 실행하면 그림 3과 같은 출력이 표시된다.


그림 3. Java 컬렉션 클래스를 사용한 웹 브라우저 출력

이 PHP 스크립트에서 수행하는 작업은 다음과 같다.

  • Java HashMap 클래스의 인스턴스를 작성한다.
  • Java Bridge!가 포함된 문자열을 맵에 저장한다.
  • Java 및 PHP 형식 간의 상호 운용성을 강조 표시한다.
  • 아래 코드와 같이 PHP 배열을 작성하고 Java 맵에 저장한다.
    $array = array(1, 2, 3, 4, 5);
    $map->put("stuff", $array);
    

맵에 대해 put 호출이 발생하면 PHP 배열이 가장 유사한 Java 형식으로 변환되는데 이 경우에는 Java Map으로 변환된다. 이와 비슷하게 get 호출은 $map의 값을 읽어서 일반적인 PHP 배열로 변환한다. 이 작업을 수행하는 데 복사 작업이 필요 없는 이유는 PHP 배열에 PHP 배열과 Java 맵이라는 두 가지 특성이 모두 있기 때문이다.




위로


Java 컬렉션 반복하기

MoreJava.php 스크립트를 다음 코드로 바꾼다.

<?php
	$list = new Java("java.util.ArrayList");
	var_dump($list);
	$date = new Java("java.util.Date", 70, 9, 4);
	echo "<br/>";
	
	$list->add("Java Bridge!");
	$list->add($date);
	$list->add(array(1, 2, 3, 4, 5));
	
	$iterator = $list->iterator();
	while ($iterator->hasNext() == TRUE) { 
	     var_dump($iterator->next()); echo "<br/>";
	}
?>

이제 브라우저에서 http://localhost:8080/MoreJava.php를 실행하면 그림 4와 같은 출력이 표시된다.


그림 4. Java 컬렉션을 반복한 웹 브라우저 출력

이 예제에서는 PHP에서 Java ArrayList 클래스를 사용하는 방법을 보여 준다. 또한 ArrayList에서 반복기를 가져와서 컬렉션을 처음부터 끝까지 검색한다. 반복기의 컨텐츠는 순서대로 작성된다. 즉, 문자열 Java Bridge!부터 시작하여 Java Date 오브젝트를 거친 다음 5개의 숫자가 포함된 PHP 배열로 끝난다.




위로


정적 메소드 및 필드에 액세스하기

정적 메소드 및 필드는 JavaClass를 통해 액세스할 수 있다. 이는 클래스 이름을 직접 사용하여 정적 메소드 및 필드에 액세스하는 Java와는 약간 다른 방법이다. 다음 코드에서는 java.lang.SystemcurrentTimeMillis를 호출하는 방법을 보여 준다.

<?php
	$system = new JavaClass("java.lang.System");
	var_dump($system);
	echo("</br>Current time: ".
		$system->currentTimeMillis()."</br>");
?>

그림 5에서는 브라우저에서 이 스크립트를 실행한 출력을 보여 준다.


그림 5. 정적 메소드에 액세스하는 웹 브라우저 출력

정적 필드에 액세스하는 방법도 유사하다. 다음 코드에서는 java.lang.Integer 클래스의 MIN_VALUE 정적 필드를 표시한다.

<?php
	$integerClass = new JavaClass("java.lang.Integer");
	var_dump($integerClass->MIN_VALUE);
?>

그림 6에서는 브라우저에서 이 스크립트를 실행한 출력을 보여 준다.


그림 6. 정적 필드에 액세스하는 웹 브라우저 출력




위로


PHP에서 Java 예외 처리하기

Java Bridge는 Java 예외를 PHP 스크립트에서 처리되는 제네릭 PHP 예외 클래스인 JavaException의 인스턴스로 변환한다. 다음 코드에서는 java.lang.SystemgetProperty에 대한 잘못된 호출을 보여 준다.

<?php
	try { 
		$system = new JavaClass("java.lang.System");
    		$system->getProperty(FALSE);
	} catch (JavaException $exception) { 
	    echo "Cause: ".$exception->getCause();
	}
?>

그림 7에서는 브라우저에서 이 스크립트를 실행한 출력을 보여 준다.


그림 7. Java 예외를 처리하는 웹 브라우저 출력

WebSphere sMash 1.0의 경우 getCause 메소드는 원인이 되는 예외 자체가 아닌 기본 Java 예외의 클래스 이름을 리턴했지만 최신 Project Zero 빌드에서는 실제 Java 예외를 리턴하도록 수정되었다.




위로


Java에서 PHP로 형식 변환

표 1에서는 Java 형식이 PHP 형식으로 변환되는 방법을 보여 준다. 일반적인 원칙은 유실 가능성이 가장 작은 형식으로 변환하는 것이다. 예를 들어 intbyte로 변환한다. 또한 변환은 Integerint와 같은 boxed 및 unboxed Java 형식에 동등하게 적용된다.


표 1. Java에서 PHP로 형식 변환
Java 형식 PHP 형식 설명
null null  
Integer/int int  
Double/double double  
Boolean/boolean bool  
Byte/byte int  
Character/char int  
Short/short int  
Long/long int  
Float/float double  
byte[] string  
String string PHP 문자열은 런타임 인코딩을 사용하여 인코딩된다.
Map array 중첩된 맵을 비롯한 개별 요소의 형식은 이 표에 따라 변환된다.
Object[] array 배열 변환에서 자세한 정보를 볼 수 있다.
기타 해당 사항 없음 Java Bridge에 의해 랩핑된 형식으로 제네릭 PHP 오브젝트가 된다.

Project Zero 웹 사이트에서 형식 변환에 대한 추가 정보를 볼 수 있다.




위로


Java Bridge 제한 사항

Java Bridge를 사용하면 PHP 스크립트에서 Java 클래스를 쉽게 사용할 수 있다. 그러나 여기에 그치지 않고 PHP 스크립트에는 없는 몇 가지 고급 기능도 제공한다. 이들 기능 중 가장 중요한 기능은 오버로드된 메소드를 안정적으로 호출하는 기능이다.

Java Bridge는 제공된 인수의 수을 고려하여 메소드나 생성자를 선택한다. 둘 이상의 가능성이 존재할 경우 Java Bridge는 첫 번째 가능성을 선택해서 시도한다. 이는 매우 단순한 방법이며 생성자나 메소드가 잘못된 인수 형식을 사용하여 호출될 경우 예외가 발생하게 된다.

서명을 사용하여 오버로드 선택하기

최신 Project Zero 빌드(WebSphere sMash 1.0에서는 사용할 수 없음)에서는 새로운 JavaSignature 클래스의 추가로 인해 적합한 오버로드의 선택과 관련된 문제가 해결되었다. JavaSignature를 사용하면 다음과 같이 스크립트에서 검색할 인수 형식을 정의하여 호출되는 생성자 또는 메소드를 정확히 지정할 수 있다.

<?php
	$signature = new JavaSignature(JAVA_STRING);
	$string = new Java("java.lang.String", 
          $signature, "Hello World!");

	var_dump($string->toLowerCase());
	var_dump($string->split(" "));
	var_dump($string->toUpperCase());
?>

JavaSignature에 대한 인수에는 다음과 같은 PHP 상수가 사용된다.

  • JAVA_BOOLEAN
  • JAVA_BYTE
  • JAVA_CHAR
  • JAVA_SHORT
  • JAVA_INT
  • JAVA_LONG
  • JAVA_FLOAT
  • JAVA_DOUBLE
  • JAVA_STRING
  • JAVA_OBJECT

이전 예제에서는 java.lang.String 클래스의 생성자를 선택하며 이 생성자는 단일 Java String을 인수(JAVA_STRING)로 받는다. 여러 인수를 사용할 경우에는 newJavaSignature(JAVA_STRING, JAVA_INT)와 같이 각 인수를 쉼표로 구분한다. JAVA_ARRAY 한정자를 사용하여 Java 형식의 배열을 지정할 수 있다. 예를 들어, newJavaSignature(JAVA_STRING | JAVA_ARRAY)는 문자열 배열을 선택한다.

다음 코드에서는 java.lang.StringvalueOf 메소드에 대한 오버로드를 선택하는 JavaSignature를 보여 준다. 그리고 서명을 메소드 호출의 첫 번째 인수로 전달하는 방법도 볼 수 있다. 이를 통해 Java Bridge가 서명을 확인할 위치를 알게 된다.

<?php
	$class = new JavaClass("java.lang.String");
	$signature = new JavaSignature(JAVA_INT);
	var_dump($class->valueOf($signature, 1234567890));
?>

대/소문자 구분 메소드 이름

PHP의 메소드는 대/소문자를 구분하지 않지만 Java는 대/소문자를 구분한다. Java Bridge에서도 대/소문자를 구분하므로 PHP 메소드 이름은 Java 메소드 이름과 정확히 일치해야 한다.

정적 메소드 및 필드

Java 개발자는 클래스 이름(예: Integer.MAX_VALUE)을 사용하여 정적 메소드 및 필드를 호출하기도 하지만 PHP에서는 이 기능이 아직 지원되지 않고 있으므로 JavaClass를 사용해야 한다. 스크립트에서 JavaClass의 인스턴스를 작성한 후 이 인스턴스를 사용하여 정적 메소드를 호출하고 정적 필드에 액세스할 수 있다. 하지만 이 방법은 비인스턴스(정적) 메소드 및 필드에 액세스하기 위해 오브젝트의 인스턴스를 작성해야 하기 때문에 자주 사용되지는 않는다.

컬렉션 반복하기

앞의 샘플 코드에서 Java 컬렉션을 반복하는 방법을 살펴보았다. 이 방법은 PHP foreach 명령문에 비해 상당히 많은 시간이 걸리기는 하지만 리소스를 적게 사용한다. Java Bridge는 Java 반복기와 PHP foreach 명령문을 통합하지 않는다. 다음 코드에서는 PHP에서 Java 반복기를 사용하는 방법을 보여 준다.

$iterator = $list->iterator();
while ($iterator->hasNext() == TRUE) { 
	 var_dump($iterator->next()); echo "<br/>";
}




위로


실제 예제 구현하기

다음 섹션에서는 앞의 섹션들에서 다룬 모든 내용을 통합하여 Java Bridge를 실제로 사용하는 예제를 설명한다. 이 예제에서는 Apache Lucene을 사용하여 파일을 인덱싱 및 검색할 수 있는 간단한 검색 엔진을 PHP로 작성한다. Apache Lucene은 전체 기능을 제공하는 고성능 텍스트 검색 엔진 라이브러리로 모든 코드가 Java로 작성되었으며 전체 텍스트 검색이 필요한 거의 모든 애플리케이션 특히, 교차 플랫폼 애플리케이션에 적합하다. 자세한 정보는 Apache Lucene 사이트에서 확인할 수 있다.

인덱스 작성하기

무엇보다도 먼저 Lucene을 마련해야 한다. 이 예제에서는 최신 버전의 Lucene이 완벽하게 작동하기는 하지만 뒷부분에서 Lucene 2.2.0을 기반으로 하는 Lucene의 PHP 구현과 비교할 계획이므로 최신 버전 대신 Lucene 2.2.0을 사용한다.

  1. lucene-2.2.0.tar.gz를 다운로드한다. 예를 들어, 미러 사이트인 http://mirror.cc.columbia.edu/pub/software/apache/lucene/java/archive/에서 다운로드한다.
  2. 파일을 압축 풀기하거나 tar -xvzf lucene-2.2.0.tar.gz를 실행한다.
  3. 두 개의 JAR 파일(lucene-core-2.2.0.jarlucene-demos-2.2.0.jar)을 찾는다.

다음 단계에서는 Lucene 검색 인덱스를 작성하는 PHP 스크립트를 작성한다.

  1. Java Perspective에서 File -> New -> Other를 선택하여 새 애플리케이션을 작성한다. WebSphere Smash PHP Application을 선택하고 Lucene이라는 이름을 지정한다.
  2. public 폴더를 마우스 오른쪽 단추로 클릭하고 New -> File을 선택한다.
  3. 파일의 이름을 index.php로 지정하고 Finish를 클릭한다.
  4. 앞에서 찾은 두 개의 Lucene JAR 파일을 Lucene/lib 디렉토리에 복사한다.
  5. WebSphere sMash에서 Lucene Java 라이브러리가 사용되는지 확인하기 위해 프로젝트 이름 Lucene을 마우스 오른쪽 단추로 클릭하고 WebSphere sMash Tools -> Resolve를 선택한다.
  6. 다음 코드를 파일에 추가한다.
    <html>
    <head>
       <title>Search Index</title>
    </head>
    <body>
    	<form name="input" action="/index.php" method="POST">
    		<label for="directory">Directory:</label>
       	    <input type="text" name="directory">
    		<label for="extension">File Extension:</label>
        	<input type="text" name="extension">
          	<input type="submit" name="action" value="Index!">
       	</form>   
    </body>
    </html>
    

  7. 프로젝트 이름 Lucene을 마우스 오른쪽 단추로 클릭하고 WebSphere sMash Application -> Run을 선택하여 애플리케이션을 실행한다. 웹 브라우저에서 로컬 서버(예: http://localhost:8080/index.php)를 실행한다. 그러면 그림 8과 같은 페이지가 표시된다.

    그림 8. 디렉토리 및 파일 확장자 선택 페이지


  8. 아직 추가할 코드가 남아 있으므로 인덱싱하려고 시도하면 안된다. 결과적으로 양식이 제출되면 PHP 스크립트가 Lucene 검색 인덱스를 작성한 후 디렉토리에 있는 파일 중에서 확장명이 일치하는 모든 파일을 사용하여 인덱스를 채우게 된다. 이 스크립트는 시작 디렉토리부터 재귀적으로 내려가면서 파일을 추가한다.
  9. 이제 다음 PHP 코드를 index.php에 추가한다.
    <?php
    
    $directory = dirname(__FILE__)."/../index";
    if (file_exists($directory) === FALSE) {
    	mkdir($directory);
    }
    
    define("INDEX_DIRECTORY", $directory);
    
    try {	
    	$extension = zget('/request/params/extension');
    	if (strlen($extension) > 0) {
    		$directory = zget('/request/params/directory');
    		if (strlen($directory) > 0) {
    			index_directory($directory, $extension);
    		}
    	}
    } catch (JavaException $exception) {
    	echo "Index creation failed [".
    	$exception->getMessage()."]</br>";	
    }
    ?>
    

  10. 아직 완료되지 않았으므로 실행하면 안된다. 이 코드는 Global Context에서 양식 변수를 가져온 후 변수가 채워져 있는지 확인한다. 변수가 채워져 있으면 index_directory 함수를 호출한다. 이 함수는 일치하는 파일을 Lucene 검색 인덱스에 추가하는 기능을 수행한다. 이 함수에 대한 자세한 내용은 뒷부분에서 설명한다.
  11. 이제 다음 PHP 코드를 index.php에 추가한다.
    /**
     * This creates an index from scratch and adds all the documents
     * by recursing from the directory passed in. It also checks
     * each candidate file to see if it matches the file extension.
     */
    function index_directory($path, $extension) {
        	echo "Indexing! [".$path.",".$extension."]</br>";
        	
    	// Uses the SimpleAnalyzer because we will do a performance comparison 
              with the PHP 
    	// implementation of Lucene in the Zend Framework and it is the closest match	
    	$analyser = new Java("org.apache.lucene.analysis.SimpleAnalyzer");
    	$policy = new Java("org.apache.lucene.index.KeepOnlyLastCommitDeletionPolicy");
    	
    	$file = new Java("java.io.File", INDEX_DIRECTORY, FALSE);
    	$file_directory = new JavaClass("org.apache.lucene.store.FSDirectory");
    	$directory = $file_directory->getDirectory($file);
    	
    	$writer = new Java("org.apache.lucene.index.IndexWriter", 
    		$directory, TRUE, $analyser, TRUE, $policy);
    	
    	$writer->setUseCompoundFile(FALSE);
    
    	// Insert some calls to microtime() for comparison
    	$start_time = get_microtime();	
    	recursive_index_directory($writer, $path, $extension);
    	$count = $writer->docCount();
    
    	// Lucene only matches the first 10,000 tokens by default
    	$writer->setMaxFieldLength(1000000);
    	$end_index_time = get_microtime();
    	
    	$writer->optimize();
    	$end_time = get_microtime();
    	$writer->close();	
    	
    	echo "Finished indexing [".$count." documents]</br>";
    	$t1 = $end_index_time - $start_time;
    	$t2 = $end_time - $end_index_time;
    	echo "Time to index  = $t1 </br>";
    	echo "Time to optimize  = $t2 </br>";
    }
    

    Java Lucene API에 대한 자세한 설명은 이 기사의 범위에 포함되지 않는다. 간단히 말해서 이 코드에서는 IndexWriter 오브젝트를 작성한다. 이 오브젝트는 스크립트에서 디렉토리를 재귀적으로 탐색하면서 파일을 추가할 주요 인덱싱 오브젝트이다. RAM 디스크와 같은 다양한 소스에 대해서도 인덱싱을 수행할 수 있다. 이 예제에서는 일반적인 파일 시스템에서 파일을 읽기 때문에 FSDirectory 클래스를 사용한다.

    IndexWriter가 설정되면 스크립트에서 recursive_index_directory를 호출하여 실제로 인덱싱을 수행한다. 이 함수는 IndexWriter, 시작할 디렉토리 및 후보 파일의 일치 여부를 확인할 때 사용할 파일 확장자를 매개변수로 받는다.

    다음 코드는 인덱싱 스크립트를 완료한다. 이 코드의 대부분은 디렉토리의 모든 파일을 열거하고 각 파일을 차례로 처리하는 일반적인 용도의 PHP 스크립트이다. 인덱싱할 파일이 결정되면 FileDocument가 작성된다. 이는 파일에 대한 완전한 경로로 설정되며 IndexWriter에 파일을 추가한다.
    /**
     * Processes a file by adding it to the indexer.
     */
    function index_file($writer, $path) {
    	echo "Indexing file [".$path."]</br>";
    
    	try {		
    		// A few of the files we indexed in the examples have non
    		// UTF-8 characters so we just skip indexing those files!
    		
    		$file = new Java("java.io.File", $path, FALSE);
    		$file_document = new JavaClass("org.apache.lucene.demo.FileDocument");
    		$document = $file_document->Document($file);
    		$writer->addDocument($document);
    
    	} catch (JavaException $exception) {
    		echo "Invalid characters in file!\n";
    	}	
    }
    
    function get_microtime(){
    	list($part_one,$part_two) = explode(' ',microtime());
    	return ((float) $part_one + (float) $part_two);
    }
    
    /**
     * Indexes all matching files (by extension) in the directory tree. 
     */
    function recursive_index_directory($writer, $path, $extension) {
        echo "Indexing directory [".$path."]</br>";
    	
        // Remove any trailing slash first
        if (substr($path, -1) == '/') {
            $path = substr($path, 0, -1);
        }
        
        // Make sure the directory is valid
        if (is_dir($path) == TRUE) {
    	    if (is_readable($path) == TRUE) {
    		 $handle = opendir($path);
    		
    		  // Scan through the directory contents
        		  $extension_length = strlen($extension);
    		  while (FALSE !== ($item = readdir($handle))) {
    		    if ($item != '.') {
    		        if ($item != '..') {
    		        $index_path = ($path.'/'.$item);
    		        if (is_dir($index_path) == TRUE) {
    		             recursive_index_directory(
    			        $writer, $index_path, $extension);
    		   } else {
    
    		         $position = strpos(strtolower($index_path), $extension);
    		                	
    		         // Very rough and ready way to check for trailing extension!
    		         if ($position == (strlen($index_path)-$extension_length)) {
    			  index_file($writer, $index_path, $extension);
    		                	}
    		                }
    		            }
    		        }
    		    }		
    		    closedir($handle);
    	    }
        }
        return TRUE;
    }
    

  12. 그림 9와 같이 웹 브라우저에서 스크립트를 실행하고 양식 변수를 채운다.

    그림 9. 디렉토리를 인덱싱한 웹 브라우저 출력


  13. Index!를 클릭한다. 그러면 선택한 파일이 인덱싱된다. 위 예제에서는 스크립트가 일부 C 소스 코드를 가리키고 5개의 소스 파일을 인덱싱한다. Eclipse 프로젝트를 새로 고치면 Index라는 새 디렉토리가 표시된다. 이 디렉토리에는 Lucene 검색 엔진에 의해 생성된 검색 인덱스 파일이 들어 있다(그림 10 참조).

    그림 10. WebSphere sMash 애플리케이션의 디렉토리 구조


애플리케이션에 검색 쿼리 추가하기

이제 마지막으로 사용자가 인덱스에 대한 검색을 실행할 수 있는 양식을 작성할 단계이다.

  1. public 폴더를 마우스 오른쪽 단추로 클릭하고 New -> File을 선택한다.
  2. 파일의 이름을 search.php로 지정하고 Finish를 클릭한다.
  3. 다음 코드를 파일에 추가한다.
    <html>
    <head>
       <title>Query</title>
    </head>
    <body>
    	<form name="input" action="/search.php" method="POST">
    		<label for="query">Search Query:</label>
       	    <input type="text" name="query">
          	<input type="submit" name="action" value="Search!">
       	</form>   
    </body>
    </html>
    

  4. 이 스크립트를 실행한다. 그러면 그림 11과 같은 웹 브라우저가 표시된다.

    그림 11. 검색 쿼리 페이지


  5. 이제 다음 PHP 코드를 search.php에 추가한다.
    <?php
    
    /**
     * This runs a search through an index already created.
     */
    function search_index($path, $query) {
    	echo "Searching for [".$query."]</br>";
    
    	$file = new Java("java.io.File", $path, FALSE);
    	$file_directory = new JavaClass("org.apache.lucene.store.FSDirectory");
    	$directory = $file_directory->getDirectory($file);
    	$searcher = new Java("org.apache.lucene.search.IndexSearcher", $directory);	
    	$analyser = new Java("org.apache.lucene.analysis.SimpleAnalyzer");
    	$parser = new Java("org.apache.lucene.queryParser.QueryParser", 
    		"contents", $analyser);
    		
    	$parsed_query = $parser->parse($query);	
    	$hits = $searcher->search($parsed_query);	
    	$count = $hits->length();
    	for ($index = 0; $index < $count; $index++) {
    		$document = $hits->doc($index);
    		echo $index.") ".$document->get("path")."</br>";
    	}
    	echo "</br>Finished searching [".$count." hits]</br>";
    }
    
    try {	
    	$directory = dirname(__FILE__)."/../index";
    	define("INDEX_DIRECTORY", $directory);
    	$query = zget('/request/params/query');
    	if (strlen($query) > 0) {
    		search_index($directory, $query);
    	}
    } catch (JavaException $exception) {
    	echo "Index search failed [".$exception->getMessage()."]</br>";	
    }
    ?>
    

    앞에서와 마찬가지로 이 스크립트에서는 여러 Lucene 클래스를 사용한다. 이 스크립트의 핵심은 index.php에서 사용한 IndexWriter 클래스 대신 IndexSearcher를 사용한다는 것이다. 그리고 이 스크립트는 앞에서 인덱스 파일을 작성했던 디렉토리와 같은 디렉토리를 사용하여 구성된다. 사용자가 양식에 입력한 문자열은 쿼리 오브젝트를 작성하는 데 사용된다. Lucene QueryParser를 사용하면 쿼리 문자열을 쉽게 구문 분석할 수 있다.

    그런 다음 구문 분석된 쿼리를 사용하여 IndexSearcher에서 검색을 실행한다. 일치하는 항목의 목록이 리턴되면 항목이 열거되면서 각 항목에 대한 경로가 함께 표시된다.
  6. 그림 12와 같이 웹 브라우저에서 search.php를 실행하고 검색 용어를 입력한다.

    그림 12. 검색 쿼리를 실행한 웹 브라우저 출력


이 예제에서는 "TSRM"과 "int"라는 키워드와 일치하는 5개의 항목이 검색되었다. Lucene은 강력한 쿼리 구문을 통해 다양한 검색 용어를 지원할 수 있다. 가능한 검색 쿼리에 대한 자세한 정보는 Apache Lucene 사이트에서 확인할 수 있다.

성능 비교

index.php를 추가했던 소스 코드를 자세히 보았다면 microtime에 대한 호출과 주석이 있다는 것을 알고 있을 것이다. 이러한 호출과 주석은 성능을 검사하기 위한 것이었다.

이 기사에서는 간단하게 소요 시간을 검사했다. 즉, 세 가지 소프트웨어를 사용하여 인덱스를 작성하는 데 걸린 시간을 비교해 보았다.

  • WebSphere sMash Java Bridge를 통해 호출한 Lucene의 Java 구현
  • Java 애플리케이션에서 호출한 Java Lucene
  • Zend Framework에서 호출한 Lucene의 PHP 구현

공정한 비교를 위해 이 기사에서는 Zend 구현의 기반이 되는 Lucene 버전 2.2.0을 사용했으며 Lucene SimpleAnalyser도 사용했다. Zend 구현에 대한 자세한 설명은 이 기사의 범위에 해당하지 않는다. 하지만 이 구현에는 Lucene 코드가 올바르게 이식되었기 때문에 Java 버전에서 생성된 것과 동일한 형식의 인덱스가 생성된다.

성능 비교를 위해 PHP 5.3 소스 트리 아래에 있는 모든 PHP 테스트 스크립트(*.phpt 파일)를 인덱싱했으며 인덱스를 작성하고 최적화하는 데 걸린 시간은 표 2와 같다.


표 2. Lucene 검색 성능 비교
기술 시간(초)
WebSpere sMash Java Bridge 9
Java Lucene 8
Zend Search Lucene 200

이 테스트를 보면 이러한 기술을 "기본 상태"로 사용하여 시간을 쉽게 비교할 수 있다는 것을 알 수 있다. 시간을 측정하는 동안 Java JIT를 사용했으며 이로 인해 Lucene과 같은 애플리케이션에서는 실행 시간에 상당한 차이가 발생한다.

실행 시간이 길다는 이유 때문에 Zend 구현을 사용하지 않는 개발자는 없다. 실제로 Java를 사용하지 않는 개발자의 주 개발 언어가 PHP인 경우 PHP로 작성된 검색 엔진을 사용하면 많은 장점을 얻을 수 있다. 코드를 쉽게 이해하고 수정할 수 있다는 장점이 성능이 낮다는 단점을 충분히 상쇄하고도 남기 때문이다.

PHP와 Java Bridge를 사용한 결과와 Java 애플리케이션을 사용한 결과를 비교하면 더 흥미로운 사실을 파악할 수 있다. 두 경우의 소요 시간이 거의 비슷할 뿐만 아니라 Java Bridge를 실행할 때와 마찬가지로 Java VM에서 PHP를 실행할 때도 많은 시간이 걸리지 않는다는 것을 알 수 있다.

물론 다른 PHP to Java 브리지도 있다. 예를 들어, Zend Platform의 상업용 구현과 sourceforge.net에서 제공하는 오픈 소스 구현이 있다. 이 기사에서는 이러한 구현을 사용하지 않았지만 이러한 구현이 존재한다는 사실이 알고리즘 성능이 좋은 Java를 사용하는 것과 사용하기 쉬운 PHP를 활용하는 것에 대한 논쟁에 많은 도움이 된다.

이러한 테스트를 반복하다 보면 작성된 인덱스에 약간의 차이가 있다는 것을 알 수 있다. Zend 구현의 유용한 특징 중 하나는 Java 구현과 완전히 같은 형식의 인덱스를 작성한다는 것이다. 이는 곧 표준 Java 도구(예를 들어, Luke 사이트에서 다운로드할 수 있는 Luke)를 통해 인덱스를 검사할 수 있다는 것을 의미한다. 이러한 차이는 모두 비교적 설명하기가 쉬우며 시간 비교에 영향을 미치지 않는다. 예를 들어, PHP 및 Java 분석기 사이에도 약간의 차이가 있다.




위로


결론

이 기사에서 살펴본 내용은 다음과 같다.

  • PHP 및 WebSphere sMash에서 애플리케이션을 작성했다.
  • Java Bridge를 사용하여 Java 오브젝트를 작성하고 호출했다.
  • PHP 스크립트에서 Java 컬렉션을 사용하는 방법을 살펴보았다.
  • Java Bridge를 통해 형식 변환 및 예외 처리를 수행하는 방법을 살펴보았다.
  • Java Lucene 라이브러리를 기반으로 하는 검색 엔진을 개발했다.
  • Java Lucene 라이브러리의 성능을 살펴보았다.

이제 이 기사를 모두 완료했으므로 Java 라이브러리와 PHP 스크립트를 다양한 방법으로 활용해 볼 수 있다. WebSphere sMash에서 Java 라이브러리와 PHP를 결합해 볼 수도 있을 것이다. Project Zero forums에서 작업에 필요한 많은 정보를 얻을 수 있다. 참고자료에 있는 WebSphere sMash Developer's Guide에서 Zero Global Context 및 기타 관련 항목에 대한 자세한 정보를 볼 수 있다.

감사의 인사

이 기사를 집필하는 데 많은 도움을 주신 ZSL Inc.의 Naveen Noel Jakkamsetti에게 감사의 뜻을 전한다.



참고자료

교육
  • Project Zero, WebSphere sMash 및 PHP 시작하기에서 WebSphere sMash를 다운로드하고 PHP 애플리케이션을 작성하는 방법을 볼 수 있다.

  • Project Zero 커뮤니티에 참여하고 싶거나 진행 상황을 알고 싶은 경우에는 Project Zero 웹 사이트에서 제공하는 블로그, 포럼 및 회원 개발자 커뮤니티를 둘러보기 바란다.

  • WebSphere sMash documentation에서 FAQ, 튜토리얼 및 데모를 비롯하여 이 프로젝트와 관련된 모든 문서를 찾아볼 수 있다.

  • IBM Fellow이자 WebSphere CTO인 Jerry Cuomo와의 팟캐스트 인터뷰 및 토론에서는 WebSphere sMash 및 Web 2.0 개발에 미치는 WebSphere sMash의 영향에 대한 그의 깊은 식견을 들어볼 수 있다.

  • developerWorks PHP zone에서 포괄적인 PHP 프로젝트 리소스를 확인할 수 있다.

  • developerWorks 웹 개발 영역에서는 Web 2.0 기술을 사용하는 웹 개발자를 위한 리소스를 제공한다.

  • developerWorks Ajax resource center에는 Ajax 애플리케이션 개발을 시작하는 데 도움이 되는 컨텐츠와 도구가 들어 있다.


제품 및 기술 얻기
  • Project Zero 사이트에서 WebSphere sMash for PHP를 다운로드하고 설치하는 방법에 대한 자세한 지시 사항을 확인할 수 있다.


토론


필자소개

Photo: Ant Phillips

Ant Phillips는 영국의 허슬리에 있는 IBM Java Technology Centre에서 근무하는 소프트웨어 개발자이다. 현재는 동적 웹 애플리케이션을 생성하기 위한 단순한 환경인 WebSphere sMash에 관한 업무를 수행하고 있다. IBM 입사 전에는 영국 뉴버리에 있는 신생 기업의 기술 팀장이었다. 그 이전에는 Sony® 및 Microsoft®에서 근무하였으며 도쿄와 시애틀을 비롯한 여러 지역을 방문했다. 여가 시간에는 스포츠를 즐기거나 부인 및 두 아이와 함께 여행한다.


Photo: Zoe Slattery

Zoe Slattery는 영국의 허슬리에 있는 IBM에서 근무하는 소프트웨어 엔지니어로 오픈 소스 개발 및 PHP에 많은 관심을 갖고 있다. IT 산업의 다양한 분야에 대한 경력을 지니고 있는 그녀는 FORTRAN 프로그래머로 시작한 후 수치 연산이 많은 계산을 위한 병렬 시스템 관련 프로젝트에 참여했으며 European Centre for Medium Range Weather Forecasts의 데이터 아카이브를 관리했다. 최근에는 IBM의 Java 가상 시스템 및 Java 클래스 라이브러리의 오픈 소스 구현 업무를 수행하는 개발 팀을 관리했다.




기사에 대한 평가


보다 나은 서비스를 제공하기 위함이오니 잠시 짬을 내어 이 양식을 제출하여 주십시오.



 


 


 


이 문서 북마킹 하기

mar.gar.in mar.gar.in naver naver eolin eolin del.icio.us del.icio.us



developerWorks 콘텐트를 다른 사이트에 전재하기:
developerWorks 콘텐트에 대한 저작권은 IBM에 있습니다. IBM의 서면 허가나 원본 저자의 허락이 없이는 전재를 금합니다. 저희 콘텐트를 전재하시려면 IBM developerWorks 담당자 에게 문의하십시오.
    IBM 소개 개인정보 보호정책 문의