달력

42024  이전 다음

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
Posted by tornado
|
Core Java Technologies Technical Tips
 Core Java Technologies Tech Tip에 오신 여러분을 환영합니다
Core Java Technologies
TECHNICAL TIPS
2005년 10월 18일자
 이번 호에서는,

» CookieHandler를 이용한 쿠키 관리
» 테크팁 퀴즈 정답

을 다루게 됩니다.



CookieHandler를 이용한 쿠키 관리
 

자바 플랫폼의 경우, URL을 통한 오브젝트 액세스는 일련의 프로토콜 핸들러에 의해 관리된다. URL의 첫 부분은 사용되는 프로토콜을 알려주는데, 예를 들어 URL이 file:로 시작되면 로컬 파일 시스템 상에서 리소스를 액세스할 수 있다. 또, URL이 http:로 시작되면 인터넷을 통해 리소스 액세스가 이루어진다. 한편, J2SE 5.0은 시스템 내에 반드시 존재해야 하는 프로토콜 핸들러(http, https, file, ftp, jar 등)를 정의한다.

J2SE 5.0은 http 프로토콜 핸들러 구현의 일부로 CookieHandler를 추가하는데, 이 클래스는 쿠키를 통해 시스템 내에서 상태(state)가 어떻게 관리될 수 있는지를 보여준다. 쿠키는 브라우저의 캐시에 저장된 데이터의 단편이며, 한번 방문한 웹 사이트를 다시 방문할 경우 쿠키 데이터를 이용하여 재방문자임을 식별한다. 쿠키는 가령 온라인 쇼핑 카트 같은 상태 정보를 기억할 수 있게 해준다. 쿠키에는 브라우저를 종료할 때까지 단일 웹 세션 동안 데이터를 보유하는 단기 쿠키와 1주 또는 1년 동안 데이터를 보유하는 장기 쿠키가 있다.

J2SE 5.0에서 기본값으로 설치되는 핸들러는 없으나, 핸들러를 등록하여 애플리케이션이 쿠키를 기억했다가 http 접속 시에 이를 반송하도록 할 수는 있다.

CookieHandler 클래스는 두 쌍의 관련 메소드를 가지는 추상 클래스이다. 첫 번째 쌍의 메소드는 현재 설치된 핸들러를 찾아내고 각자의 핸들러를 설치할 수 있게 한다.

  • getDefault()
  • setDefault(CookieHandler)

보안 매니저가 설치된 애플리케이션의 경우, 핸들러를 얻고 이를 설정하려면 특별 허가를 받아야 한다. 현재의 핸들러를 제거하려면 핸들러로 null을 입력한다. 또한 앞서 얘기했듯이 기본값으로 설정되어 있는 핸들러는 없다.

두 번째 쌍의 메소드는 각자가 관리하는 쿠키 캐시로부터 쿠키를 얻고 이를 설정할 수 있게 한다.

  • get(URI uri, Map<String, List<String>> requestHeaders)
  • put(URI uri, Map<String, List<String>> responseHeaders)

get() 메소드는 캐시에서 저장된 쿠기를 검색하여 requestHeaders를 추가하고, put() 메소드는 응답 헤더에서 쿠키를 찾아내어 캐시에 저장한다.

여기서 보듯이 핸들러를 작성하는 일은 실제로는 간단하다. 그러나 캐시를 정의하는 데는 약간의 추가 작업이 더 필요하다. 일례로, 커스텀 CookieHandler, 쿠키 캐시, 테스트 프로그램을 사용해 보기로 하자. 테스트 프로그램은 아래와 같은 형태를 띠고 있다.

   import java.io.*;   import java.net.*;   import java.util.*;   public class Fetch {     public static void main(String args[]) throws Exception {       if (args.length == 0) {         System.err.println("URL missing");         System.exit(-1);       }       String urlString = args[0];       CookieHandler.setDefault(new ListCookieHandler());       URL url = new URL(urlString);       URLConnection connection = url.openConnection();       Object obj = connection.getContent();       url = new URL(urlString);       connection = url.openConnection();       obj = connection.getContent();     }   }

먼저 이 프로그램은 간략하게 정의될 ListCookieHandler를 작성하고 설치한다. 그런 다음 URL(명령어 라인에서 입력)의 접속을 열어 내용을 읽는다. 이어서 프로그램은 또 다른 URL의 접속을 열고 동일한 내용을 읽는다. 첫 번째 내용을 읽을 때 응답에는 저장될 쿠키가, 두 번째 요청에는 앞서 저장된 쿠키가 포함된다.

이제 이것을 관리하는 방법에 대해 알아보기로 하자. 처음에는 URLConnection 클래스를 이용한다. 웹 상의 리소스는 URL을 통해 액세스할 수 있으며, URL 작성 후에는 URLConnection 클래스의 도움을 받아 사이트와의 통신을 위한 인풋 또는 아웃풋 스트림을 얻을 수 있다.

   String urlString = ...;   URL url = new URL(urlString);   URLConnection connection = url.openConnection();   InputStream is = connection.getInputStream();   // .. read content from stream

접속으로부터 이용 가능한 정보에는 일련의 헤더들이 포함될 수 있는데, 이는 사용중인 프로토콜에 의해 결정된다. 헤더를 찾으려면 URLConnection 클래스를 사용하면 된다. 한편, 클래스는 헤더 정보 검색을 위한 다양한 메소드를 가지는데, 여기에는 다음 사항들이 포함된다.

  • getHeaderFields() - 가용한 필드의 Map을 얻는다.
  • getHeaderField(String name) - 이름 별로 헤더 필드를 얻는다.
  • getHeaderFieldDate(String name, long default) - 날짜로 된 헤더 필드를 얻는다.
  • getHeaderFieldInt(String name, int default) - 숫자로 된 헤더 필드를 얻는다.
  • getHeaderFieldKey(int n) or getHeaderField(int n) - 위치 별로 헤더 필드를 얻는다.

일례로, 다음 프로그램은 주어진 URL의 모든 헤더를 열거한다

   import java.net.*;   import java.util.*;   public class ListHeaders {     public static void main(String args[]) throws Exception {       if (args.length == 0) {         System.err.println("URL missing");       }       String urlString = args[0];       URL url = new URL(urlString);       URLConnection connection = url.openConnection();       Map<String,List<String>> headerFields =          connection.getHeaderFields();       Set<String> set = headerFields.keySet();       Iterator<String> itor = set.iterator();       while (itor.hasNext()) {         String key = itor.next();         System.out.println("Key: " + key + " / " +            headerFields.get(key));       }     }   }

ListHeaders 프로그램은 가령 http://java.sun.com 같은 URL을 아규먼트로 취하고 사이트로부터 수신한 모든 헤더를 표시한다. 각 헤더는 아래의 형태로 표시된다.

   Key: <key> / [<value>]

따라서 다음을 입력하면,

  >> java ListHeaders http://java.sun.com

다음과 유사한 내용이 표시되어야 한다.

   Key: Set-Cookie / [SUN_ID=192.168.0.1:269421125489956;    EXPIRES=Wednesday, 31- Dec-2025 23:59:59 GMT;    DOMAIN=.sun.com; PATH=/]   Key: Set-cookie /    [JSESSIONID=688047FA45065E07D8792CF650B8F0EA;Path=/]   Key: null / [HTTP/1.1 200 OK]   Key: Transfer-encoding / [chunked]   Key: Date / [Wed, 31 Aug 2005 12:05:56 GMT]   Key: Server / [Sun-ONE-Web-Server/6.1]   Key: Content-type / [text/html;charset=ISO-8859-1]   

(위에 표시된 결과에서 긴 행은 수동으로 줄바꿈한 것임)

이는 해당 URL에 대한 헤더들만을 표시하며, 그곳에 위치한 HTML 페이지는 표시하지 않는다. 표시되는 정보에는 사이트에서 사용하는 웹 서버와 로컬 시스템의 날짜 및 시간이 포함되는 사실에 유의할 것. 아울러 2개의 ‘Set-Cookie’ 행에도 유의해야 한다. 이들은 쿠키와 관련된 헤더들이며, 쿠키는 헤더로부터 저장된 뒤 다음의 요청과 함께 전송될 수 있다.

이제 CookieHandler를 작성해 보자. 이를 위해서는 두 추상 메소드 CookieHandler: get() 과ㅓ put()을 구현해야 한다.

  •   public void put(    URI uri,    Map<String, List<String>> responseHeaders)      throws IOException
  •   public Map<String, List<String>> get(    URI uri,    Map<String, List<String>> requestHeaders)      throws IOException

우선 put() 메소드로 시작한다. 이 경우 응답 헤더에 포함된 모든 쿠키가 캐시에 저장된다.put()을 구현하기 위해서는 먼저 ‘Set-Cookie’ 헤더의 List를 얻어야한다. 이는 Set-cookieSet-Cookie2 같은 다른 해당 헤더로 확장될 수 있다.

   List<String> setCookieList =     responseHeaders.get("Set-Cookie");

쿠키의 리스트를 확보한 후 각 쿠키를 반복(loop)하고 저장한다. 쿠키가 이미 존재할 경우에는 기존의 것을 교체하도록 한다.

    if (setCookieList != null) {      for (String item : setCookieList) {        Cookie cookie = new Cookie(uri, item);        // Remove cookie if it already exists in cache        // New one will replace it        for (Cookie existingCookie : cache) {          ...        }        System.out.println("Adding to cache: " + cookie);        cache.add(cookie);      }    }

여기서 ‘캐시’는 데이터베이스에서 Collections Framework에서 List에 이르기까지 어떤 것이든 될 수 있다. Cookie 클래스는 나중에 정의되는데, 이는 사전 정의되는 클래스에 속하지 않는다.

본질적으로, 그것이 put() 메소드에 대해 주어진 전부이며, 응답 헤더 내의 각 쿠키에 대해 메소드는 쿠키를 캐시에 저장한다.

get() 메소드는 정반대로 작동한다. URI에 해당되는 캐시 내의 각 쿠키에 대해, get() 메소드는 이를 요청 헤더에 추가한다. 복수의 쿠키에 대해서는 콤마로 구분된(comma-delimited) 리스트를 작성한다. get() 메소드는 맵을 반환하며, 따라서 메소드는 기존의 헤더 세트로 Map 아규먼트를 취하게 된다. 그 아규먼트에 캐시 내의 해당 쿠키를 추가해야 하지만 아규먼트는 불변의 맵이며, 또 다른 불변의 맵을 반환해야만 한다. 따라서 기존의 맵을 유효한 카피에 복사한 다음 추가를 마친 후 불변의 맵을 반환해야 한다.

get() 메소드를 구현하기 위해서는 먼저 캐시를 살펴보고 일치하는 쿠키를 얻은 다음 만료된 쿠키를 모두 제거하도록 한다.

    // Retrieve all the cookies for matching URI    // Put in comma-separated list    StringBuilder cookies = new StringBuilder();    for (Cookie cookie : cache) {      // Remove cookies that have expired      if (cookie.hasExpired()) {        cache.remove(cookie);      } else if (cookie.matches(uri)) {        if (cookies.length() > 0) {          cookies.append(", ");        }        cookies.append(cookie.toString());      }    }

이 경우에도 Cookie 클래스는 간략하게 정의되는데, 여기에는 hasExpired()matches() 등 2개의 요청된 메소드가 표시되어 있다. hasExpired() 메소드는 특정 쿠키의 만료 여부를 보고하고, matches() 메소드는 쿠키가 메소드에 패스된 URI에 적합한지 여부를 보고한다.

get() 메소드의 다음 부분은 작성된 StringBuilder 오브젝트를 취하고 그 스트링필드 버전을 수정 불가능한 Map에 put한다(이 경우에는 해당 키 ‘Cookie’를 이용).

    // Map to return    Map<String, List<String>> cookieMap =      new HashMap<String, List<String>>(requestHeaders);    // Convert StringBuilder to List, store in map    if (cookies.length() > 0) {      List<String> list =        Collections.singletonList(cookies.toString());      cookieMap.put("Cookie", list);    }    return Collections.unmodifiableMap(cookieMap);

다음은 런타임의 정보 표시를 위해 println이 일부 추가되어 완성된 CookieHandler 정의이다.

   import java.io.*;   import java.net.*;   import java.util.*;   public class ListCookieHandler extends CookieHandler {     // "Long" term storage for cookies, not serialized so only     // for current JVM instance     private List<Cookie> cache = new LinkedList<Cookie>();     /**      * Saves all applicable cookies present in the response       * headers into cache.      * @param uri URI source of cookies      * @param responseHeaders Immutable map from field names to       * lists of field      *   values representing the response header fields returned      */     public void put(         URI uri,         Map<String, List<String>> responseHeaders)           throws IOException {       System.out.println("Cache: " + cache);       List<String> setCookieList =          responseHeaders.get("Set-Cookie");       if (setCookieList != null) {         for (String item : setCookieList) {           Cookie cookie = new Cookie(uri, item);           // Remove cookie if it already exists           // New one will replace           for (Cookie existingCookie : cache) {             if((cookie.getURI().equals(               existingCookie.getURI())) &&                (cookie.getName().equals(                  existingCookie.getName()))) {              cache.remove(existingCookie);              break;            }          }          System.out.println("Adding to cache: " + cookie);          cache.add(cookie);        }      }    }    /**     * Gets all the applicable cookies from a cookie cache for      * the specified uri in the request header.     *     * @param uri URI to send cookies to in a request     * @param requestHeaders Map from request header field names      * to lists of field values representing the current request      * headers     * @return Immutable map, with field name "Cookie" to a list      * of cookies     */    public Map<String, List<String>> get(        URI uri,        Map<String, List<String>> requestHeaders)          throws IOException {      // Retrieve all the cookies for matching URI      // Put in comma-separated list      StringBuilder cookies = new StringBuilder();      for (Cookie cookie : cache) {        // Remove cookies that have expired        if (cookie.hasExpired()) {          cache.remove(cookie);        } else if (cookie.matches(uri)) {          if (cookies.length() > 0) {            cookies.append(", ");          }          cookies.append(cookie.toString());        }      }      // Map to return      Map<String, List<String>> cookieMap =        new HashMap<String, List<String>>(requestHeaders);      // Convert StringBuilder to List, store in map      if (cookies.length() > 0) {        List<String> list =          Collections.singletonList(cookies.toString());        cookieMap.put("Cookie", list);      }        System.out.println("Cookies: " + cookieMap);    return Collections.unmodifiableMap(cookieMap);    }  }

퍼즐의 마지막 조각은 Cookie 클래스 그 자체이며, 대부분의 정보는 생성자(constructor) 내에 존재한다. 생성자 내의 정보 조각(비트)들을 uri 및 헤더 필드로부터 파싱해야 한다. 만료일에는 하나의 포맷이 사용되어야 하지만 인기 있는 웹 사이트에서는 복수의 포맷이 사용되는 경우를 볼 수 있다. 여기서는 그다지 까다로운 점은 없고, 쿠키 경로, 만료일, 도메인 등과 같은 다양한 정보 조각을 저장하기만 하면 된다.

   public Cookie(URI uri, String header) {     String attributes[] = header.split(";");     String nameValue = attributes[0].trim();     this.uri = uri;     this.name = nameValue.substring(0, nameValue.indexOf('='));     this.value = nameValue.substring(nameValue.indexOf('=')+1);     this.path = "/";     this.domain = uri.getHost();     for (int i=1; i < attributes.length; i++) {       nameValue = attributes[i].trim();       int equals = nameValue.indexOf('=');       if (equals == -1) {         continue;       }       String name = nameValue.substring(0, equals);       String value = nameValue.substring(equals+1);       if (name.equalsIgnoreCase("domain")) {         String uriDomain = uri.getHost();         if (uriDomain.equals(value)) {           this.domain = value;         } else {           if (!value.startsWith(".")) {             value = "." + value;           }           uriDomain =              uriDomain.substring(uriDomain.indexOf('.'));           if (!uriDomain.equals(value)) {             throw new IllegalArgumentException(               "Trying to set foreign cookie");           }           this.domain = value;         }       } else if (name.equalsIgnoreCase("path")) {         this.path = value;       } else if (name.equalsIgnoreCase("expires")) {         try {           this.expires = expiresFormat1.parse(value);         } catch (ParseException e) {           try {             this.expires = expiresFormat2.parse(value);           } catch (ParseException e2) {             throw new IllegalArgumentException(               "Bad date format in header: " + value);           }         }       }     }  }

클래스 내의 다른 메소드들은 단지 저장된 데이터를 반환하거나 만료 여부를 확인한다.

   public boolean hasExpired() {     if (expires == null) {       return false;     }     Date now = new Date();     return now.after(expires);   }   public String toString() {     StringBuilder result = new StringBuilder(name);     result.append("=");     result.append(value);     return result.toString();   }

쿠키가 만료된 경우에는 ‘match’가 표시되면 안 된다.

   public boolean matches(URI uri) {     if (hasExpired()) {       return false;     }     String path = uri.getPath();     if (path == null) {       path = "/";     }      return path.startsWith(this.path);   }

Cookie 스펙이 도메인과 경로 양쪽에 대해 매치를 수행할 것을 요구한다는 점에 유의해야 한다. 단순성을 위해 여기서는 경로 매치만을 확인한다.

아래는 전체 Cookie 클래스의 정의이다.

   import java.net.*;   import java.text.*;   import java.util.*;   public class Cookie {     String name;     String value;     URI uri;     String domain;     Date expires;     String path;     private static DateFormat expiresFormat1         = new SimpleDateFormat("E, dd MMM yyyy k:m:s 'GMT'", Locale.US);     private static DateFormat expiresFormat2        = new SimpleDateFormat("E, dd-MMM-yyyy k:m:s 'GMT'", Local.US);		     /**      * Construct a cookie from the URI and header fields      *      * @param uri URI for cookie      * @param header Set of attributes in header      */     public Cookie(URI uri, String header) {       String attributes[] = header.split(";");       String nameValue = attributes[0].trim();       this.uri = uri;       this.name =          nameValue.substring(0, nameValue.indexOf('='));       this.value =          nameValue.substring(nameValue.indexOf('=')+1);       this.path = "/";       this.domain = uri.getHost();       for (int i=1; i < attributes.length; i++) {         nameValue = attributes[i].trim();         int equals = nameValue.indexOf('=');         if (equals == -1) {           continue;         }         String name = nameValue.substring(0, equals);         String value = nameValue.substring(equals+1);         if (name.equalsIgnoreCase("domain")) {           String uriDomain = uri.getHost();           if (uriDomain.equals(value)) {             this.domain = value;           } else {             if (!value.startsWith(".")) {               value = "." + value;             }             uriDomain = uriDomain.substring(               uriDomain.indexOf('.'));             if (!uriDomain.equals(value)) {               throw new IllegalArgumentException(                 "Trying to set foreign cookie");             }             this.domain = value;           }         } else if (name.equalsIgnoreCase("path")) {           this.path = value;         } else if (name.equalsIgnoreCase("expires")) {           try {             this.expires = expiresFormat1.parse(value);           } catch (ParseException e) {             try {               this.expires = expiresFormat2.parse(value);             } catch (ParseException e2) {               throw new IllegalArgumentException(                 "Bad date format in header: " + value);             }           }         }       }     }     public boolean hasExpired() {       if (expires == null) {         return false;       }       Date now = new Date();       return now.after(expires);     }     public String getName() {       return name;     }     public URI getURI() {       return uri;     }     /**      * Check if cookie isn't expired and if URI matches,      * should cookie be included in response.      *      * @param uri URI to check against      * @return true if match, false otherwise      */     public boolean matches(URI uri) {       if (hasExpired()) {         return false;       }      String path = uri.getPath();       if (path == null) {         path = "/";       }       return path.startsWith(this.path);     }     public String toString() {       StringBuilder result = new StringBuilder(name);       result.append("=");       result.append(value);       return result.toString();     }   }

이제 조각들이 모두 확보되었으므로 앞의 Fetch 예제를 실행할 수 있다.

   >> java Fetch http://java.sun.com   Cookies: {Connection=[keep-alive], Host=[java.sun.com],     User-Agent=[Java/1.5.0_04], GET / HTTP/1.1=[null],     Content-type=[application/x-www-form-urlencoded],     Accept=[text/html, image/gif, image/jpeg, *; q=.2, */*; q=.2]}   Cache: []   Adding to cache: SUN_ID=192.168.0.1:235411125667328   Cookies: {Connection=[keep-alive], Host=[java.sun.com],     User-Agent=[Java/1.5.0_04], GET / HTTP/1.1=[null],     Cookie=[SUN_ID=192.168.0.1:235411125667328],     Content-type=[application/x-www-form-urlencoded],     Accept=[text/html, image/gif, image/jpeg, *; q=.2, */*; q=.2]}   Cache: [SUN_ID=192.168.0.1:235411125667328]

(위에 표시된 결과에서 긴 행은 수동으로 줄바꿈한 것임)

‘Cache’로 시작되는 행은 저장된 캐시를 나타낸다. 저장된 쿠키가 즉시 반환되지 않도록 put() 메소드 전에 get() 메소드가 어떻게 호출되는지에 대해 유의하도록 할 것.

쿠키와 URL 접속을 이용한 작업에 관해 자세히 알고 싶으면 자바 튜토리얼의 Custom Networking trail(영문)을 참조할 것. 이는 J2SE 1.4에 기반을 두고 있으므로 튜토리얼에는 아직 여기서 설명한 CookieHandler에 관한 정보가 실려 있지 않다. Java SE 6 ("Mustang")(영문) 릴리즈에서도 기본 CookieHandler 구현에 관한 내용을 찾아볼 수 있다.

Back to Top

테크팁 퀴즈 정답
 
지난 주 뉴스레터로 발송되었던 테크팁 퀴즈의 정답을 알려드립니다.
참여해주신 모든 분께 감사드리며, 당첨자는 10월 26일 SKDN 홈페이지에서 발표합니다.
  1. 다음 중 "pull"파서는?
    1. Xerces
    2. SSAX
    3. Sun Java Streaming XML Parser (SJSXP)
    4. 모두 아님

    정답 c. SJSXP(Sun Java Streaming XML Parser)
    SJSXP는 StAX(Streaming API for XML) 파서의 고속 구현으로서 일종의 pull 파서이다(이와 대조적으로, Xerces와 SSAX는 SAX 파서임). SJSXP는 파서가 문서 내에서 현재 스캔되는 장소에 sorts 포인터?이를 흔히 커서라고 한다?를 유지하는 pull 메소드를 구현한다. 우리는 SJSXP가 제공하는 2개의 API, 커서와 반복자(iterator) 중 하나를 이용하여 커서가 현재 가리키고 있는 노드를 파서에 대해 요구하면 된다. 커서 API는 XML 정보를 문자열로 반환하는 반면 반복자 API는 별개의 이벤트 오브젝트를 파서에서 읽히는 순서대로 반환한다. SJSXP에 관한 자세한 내용은 2005년 4월 29일자 J2EE 테크팁 Sun Java Streaming XML Parser 소개 참조

  2. 다음의 WSDL 바인딩 style/use 조합 중에서 WS-I 호환이 되는 것은?
    1. RPC/encoded
    2. RPC/literal
    3. Document/encoded
    4. Document/literal

    정답 b와 d
    RPC/literal과 Document/literal은 WS-I 호환이 된다. style/use 조합에 관한 자세한 내용은 2005년 8월 23일자 J2EE 테크팁 JAX-RPC 바인딩 스타일과 사용 속성 참조

  3. 다음 중 JSF(JavaServer Faces) 기술에서 유효한 바인딩 식은?
    1. #{invoice.customerName}
    2. ${invoice.date}
    3. #[customer.status == 'VIP']
    4. 모두 아님

    정답 a. #{invoice.customerName}
    바인딩 식의 신택스는 그 자체가 JavaScript의 오브젝트 액세스 신택스에 기초한 JSP(JavaServer Pages) 2.0 Expression Language에 기반을 두고 있다. JSP에서는 식들이 "${}"로 둘러싸이지만 JSF에서는 "#{}"이 사용된다. 자세한 내용은 2004년 10월 15일자 J2EE 테크팁 JSF의 값 바인딩 표현과 메서드 바인딩 표현의 내용을 참조할 것. Expression Language를 JSP 2.1로 통일하기 위해 JSF 1.2에 대해서 업데이트가 이루어졌다는 점에 유의한다. 새로이 통일된 Expression Language에 관한 자세한 내용을 보려면 테크니컬 아티클 Unified Expression Language(영문)참조.

  4. message-driven 빈에 관한 설명 중 맞는 것은?
    1. message-driven 빈의 인스턴스는 특정 클라이언트의 데이터 또는 대화 상태를 유지하지 않는다.
    2. message-driven 빈의 모든 인스턴스는 서로 동등하므로 컨테이너는 어떠한 message-driven 빈 인스턴스에도 메시지를 할당할 수 있다.
    3. 하나의 message-driven 빈이 복수 클라이언트의 메시지를 처리할 수 있다.
    4. 모두 맞음
    5. 모두 틀림

    정답 d. 모두 맞음
    모두 맞음. MDBs에 관한 자세한 내용은 2005년 5월 18일자 J2EE 테크팁 EJB 2.1로 메시지 구동 빈 이용하기의 내용 참조

  5. 스트링 리스트를 정렬하는 방법은?
    1. TreeList 클래스를 사용한다
    2. TreeMap 생성자에 List를 패스하고 맵을 반복한다
    3. Arrayssort 메소드를 호출한다
    4. Collectionssort 메소드를 호출한다

    정답 d. Collections의 정렬 메소드를 호출한다
    자세한 관련 정보는 2004년 7월 16일자 J2SE 테크팁 리스트를 분류하고 섞기 위한 COLLECTIONS의 메소드 사용하기를 참조한다. Collections Framework에 관한 부분은 이미 테크 팁을 통해 여러 차례에 걸쳐 다룬 바 있다.

  6. 다음 중 Math 클래스로 할 수 없는 연산은?
    1. 특정 수의 기수 10 로그를 계산한다
    2. 특정 String에 대한 해시코드를 생성한다
    3. 유니코드 문자의 세제곱근을 계산한다
    4. 두 점간의 유클리드 거리를 계산한다

    정답 b. 특정 String에 대한 해시코드를 생성한다.
    String의 해시코드 계산은 String 클래스에 의해 이루어지며, 다른 세 연산은 JDK 5.0에 추가된 Math 클래스 메소드를 통해 수행할 수 있다. 유니코드 문자의 세제곱근을 계산하려면 계산에 앞서 문자를 char에서 int로 변환한다. 자세한 내용은 2004년 11월 11일자 J2SE 테크팁 MATH 클래스에서의 새로운 점참조
Back to Top

본 메일은 수신을 동의한 회원님에게만 발송됩니다.
본 메일의 수신을 거부하거나 수신주소를 변경하려면 SKDN@Sun.com으로 문의 주시기 바랍니다.

SKDN(Sun Korea Developers Network)에서 J2EE/J2SE 테크팁 등 다양한 아티클들을 참고하세요.

Copyright 2003-2005 Sun Korea, Ltd. All rights reserved.


'JAVA > JSP_Servlet' 카테고리의 다른 글

[펌] JSP 2.0에서 Custom Tag활용  (0) 2005.10.25
[link] ajaxfaces.com  (0) 2005.10.25
[펌] apache.common.DbUtil & Oracle 용 RowProcess  (0) 2005.10.17
jetty site  (0) 2005.10.12
[펌] myeclipse에서 xdoclet 설명  (0) 2005.10.04
Posted by tornado
|

Oracle의 숫자형 필드의 도메인은 Number로 잡는다.

그런데 java에서는 Integer와 Double로 명시적으로 사용하고 싶었다.

그리고, DATE 형은 oracle-jdbc에서 java.sql.TimeStamp 형으로 넘어와서

Bean에 자동으로 set이 안된다.

그래서 BasicRowProcess.java를 조금 바꿔서 OracleRowProcess.java로 하고

사용하고 있다.

사용방법은 Handler생성 할때 아래와 같이 하면된다.

h = new BeanListHandler( dto.getClass(), OracleRowProcess.instance() );

 

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

OracleRowProcess.java

=============================

 

 
import java.beans.BeanInfo;
import java.beans.IntrospectionException;
import java.beans.Introspector;
import java.beans.PropertyDescriptor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.sql.SQLException;
import java.sql.Timestamp;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

import org.apache.commons.dbutils.RowProcessor;
import org.apache.log4j.Logger;
/**
 * @author 박종복 (asdkf_2000@yahoo.co.kr)
 * @version 1.0,  2005. 4. 14
 *
 */
public class OracleRowProcess implements RowProcessor {

    /**
     * Set a bean's primitive properties to these defaults when SQL NULL
     * is returned.  These are the same as the defaults that ResultSet get*
     * methods return in the event of a NULL column.
     */
    private static final Map primitiveDefaults = new HashMap();

    static {
        primitiveDefaults.put(Integer.TYPE, new Integer(0));
        primitiveDefaults.put(Short.TYPE, new Short((short) 0));
        primitiveDefaults.put(Byte.TYPE, new Byte((byte) 0));
        primitiveDefaults.put(Float.TYPE, new Float(0));
        primitiveDefaults.put(Double.TYPE, new Double(0));
        primitiveDefaults.put(Long.TYPE, new Long(0));
        primitiveDefaults.put(Boolean.TYPE, Boolean.FALSE);
        primitiveDefaults.put(Character.TYPE, new Character('\u0000'));
    }

    /**
     * Special array index that indicates there is no bean property that
     * matches a column from a ResultSet.
     */
    private static final int PROPERTY_NOT_FOUND = -1;

    /**
     * The Singleton instance of this class.
     */
    private static final OracleRowProcess instance = new OracleRowProcess();

    /**
     * Returns the Singleton instance of this class.
     *
     * @return The single instance of this class.
     */
    public static OracleRowProcess instance() {
        return instance;
    }

    /**
     * Protected constructor for BasicRowProcessor subclasses only.
     */
    protected OracleRowProcess() {
        super();
    }

    /**
     * Convert a <code>ResultSet</code> row into an <code>Object[]</code>.
     * This implementation copies column values into the array in the same
     * order they're returned from the <code>ResultSet</code>.  Array elements
     * will be set to <code>null</code> if the column was SQL NULL.
     *
     * @see org.apache.commons.dbutils.RowProcessor#toArray(java.sql.ResultSet)
     */
    public Object[] toArray(ResultSet rs) throws SQLException {
        ResultSetMetaData meta = rs.getMetaData();
        int cols = meta.getColumnCount();
        Object[] result = new Object[cols];

        for (int i = 0; i < cols; i++) {
            result[i] = rs.getObject(i + 1);
        }

        return result;
    }

    /**
     * Convert a <code>ResultSet</code> row into a JavaBean.  This
     * implementation uses reflection and <code>BeanInfo</code> classes to
     * match column names to bean property names.  Properties are matched to
     * columns based on several factors:
     * <br/>
     * <ol>
     *     <li>
     *     The class has a writable property with the same name as a column.
     *     The name comparison is case insensitive.
     *     </li>
     *
     *     <li>
     *     The property's set method parameter type matches the column
     *     type. If the data types do not match, the setter will not be called.
     *     </li>
     * </ol>
     *
     * <p>
     * Primitive bean properties are set to their defaults when SQL NULL is
     * returned from the <code>ResultSet</code>.  Numeric fields are set to 0
     * and booleans are set to false.  Object bean properties are set to
     * <code>null</code> when SQL NULL is returned.  This is the same behavior
     * as the <code>ResultSet</code> get* methods.
     * </p>
     *
     * @see org.apache.commons.dbutils.RowProcessor#toBean(java.sql.ResultSet, java.lang.Class)
     */
    public Object toBean(ResultSet rs, Class type) throws SQLException {

        PropertyDescriptor[] props = this.propertyDescriptors(type);

        ResultSetMetaData rsmd = rs.getMetaData();

        int[] columnToProperty = this.mapColumnsToProperties(rsmd, props);

        int cols = rsmd.getColumnCount();

        return this.createBean(rs, type, props, columnToProperty, cols);
    }

    /**
     * Convert a <code>ResultSet</code> into a <code>List</code> of JavaBeans. 
     * This implementation uses reflection and <code>BeanInfo</code> classes to
     * match column names to bean property names. Properties are matched to
     * columns based on several factors:
     * <br/>
     * <ol>
     *     <li>
     *     The class has a writable property with the same name as a column.
     *     The name comparison is case insensitive.
     *     </li>
     *
     *     <li>
     *     The property's set method parameter type matches the column
     *     type. If the data types do not match, the setter will not be called.
     *     </li>
     * </ol>
     *
     * <p>
     * Primitive bean properties are set to their defaults when SQL NULL is
     * returned from the <code>ResultSet</code>.  Numeric fields are set to 0
     * and booleans are set to false.  Object bean properties are set to
     * <code>null</code> when SQL NULL is returned.  This is the same behavior
     * as the <code>ResultSet</code> get* methods.
     * </p>
     *
     * @see org.apache.commons.dbutils.RowProcessor#toBeanList(java.sql.ResultSet, java.lang.Class)
     */
    public List toBeanList(ResultSet rs, Class type) throws SQLException {
        List results = new ArrayList();

        if (!rs.next()) {
            return results;
        }

        PropertyDescriptor[] props = this.propertyDescriptors(type);
        ResultSetMetaData rsmd = rs.getMetaData();
        int[] columnToProperty = this.mapColumnsToProperties(rsmd, props);
        int cols = rsmd.getColumnCount();

        do {
            results.add(this.createBean(rs, type, props, columnToProperty, cols));

        } while (rs.next());

        return results;
    }

    /**
     * Creates a new object and initializes its fields from the ResultSet.
     *
     * @param rs The result set
     * @param type The bean type (the return type of the object)
     * @param props The property descriptors
     * @param columnToProperty The column indices in the result set
     * @param cols The number of columns
     * @return An initialized object.
     * @throws SQLException If a database error occurs
     */
    private Object createBean(
        ResultSet rs,
        Class type,
        PropertyDescriptor[] props,
        int[] columnToProperty,
        int cols)
        throws SQLException {

        Object bean = this.newInstance(type);

        for (int i = 1; i <= cols; i++) {

            if (columnToProperty[i] == PROPERTY_NOT_FOUND) {
                continue;
            }
           
            Object value = rs.getObject(i);

            PropertyDescriptor prop = props[columnToProperty[i]];
            Class propType = prop.getPropertyType();

            if (propType != null && value == null && propType.isPrimitive()) {
                value = primitiveDefaults.get(propType);
            }
           
            this.callSetter(bean, prop, value);
        }

        return bean;
    }

    /**
     * The positions in the returned array represent column numbers.  The values
     * stored at each position represent the index in the PropertyDescriptor[]
     * for the bean property that matches the column name.  If no bean property
     * was found for a column, the position is set to PROPERTY_NOT_FOUND.
     *
     * @param rsmd The result set meta data containing column information
     * @param props The bean property descriptors
     * @return An int[] with column index to property index mappings.  The 0th
     * element is meaningless as column indexing starts at 1.
     *
     * @throws SQLException If a database error occurs
     */
    private int[] mapColumnsToProperties(
        ResultSetMetaData rsmd,
        PropertyDescriptor[] props)
        throws SQLException {

        int cols = rsmd.getColumnCount();
        int columnToProperty[] = new int[cols + 1];
       
        for (int col = 1; col <= cols; col++) {
            String columnName = rsmd.getColumnName(col);
            for (int i = 0; i < props.length; i++) {

                if (columnName.equalsIgnoreCase(props[i].getName())) {
                    columnToProperty[col] = i;
                    break;

                } else {
                    columnToProperty[col] = PROPERTY_NOT_FOUND;
                }
            }
        }

        return columnToProperty;
    }

    /**
     * Convert a <code>ResultSet</code> row into a <code>Map</code>.  This
     * implementation returns a <code>Map</code> with case insensitive column
     * names as keys.  Calls to <code>map.get("COL")</code> and
     * <code>map.get("col")</code> return the same value.
     * @see org.apache.commons.dbutils.RowProcessor#toMap(java.sql.ResultSet)
     */
    public Map toMap(ResultSet rs) throws SQLException {
        Map result = new CaseInsensitiveHashMap();
        ResultSetMetaData rsmd = rs.getMetaData();
        int cols = rsmd.getColumnCount();

        for (int i = 1; i <= cols; i++) {
            result.put(rsmd.getColumnName(i), rs.getObject(i));
        }

        return result;
    }

    /**
     * Calls the setter method on the target object for the given property.
     * If no setter method exists for the property, this method does nothing.
     * @param target The object to set the property on.
     * @param prop The property to set.
     * @param value The value to pass into the setter.
     * @throws SQLException if an error occurs setting the property.
     */
    private void callSetter(
        Object target,
        PropertyDescriptor prop,
        Object value)
        throws SQLException {

        Method setter = prop.getWriteMethod();

        if (setter == null) {
            return;
        }

        Class[] params = setter.getParameterTypes();
        try {

            // Don't call setter if the value object isn't the right type
            if( params[0].equals( Date.class) && value instanceof Timestamp ){
                setter.invoke(target, new Object[] { (java.util.Date)value } );
            }else if (this.isCompatibleType(value, params[0])) {
                    setter.invoke(target, new Object[] { value });
            }else if( params[0].equals( Integer.class) && value instanceof Number ){
                setter.invoke(target, new Object[] { new Integer(value.toString()) } );
            }else if( params[0].equals( Double.class ) && value instanceof Number ){
                setter.invoke(target, new Object[] { new Double(value.toString()) } );
            }

        } catch (IllegalArgumentException e) {
            throw new SQLException(
                "Cannot set " + prop.getName() + ": " + e.getMessage());

        } catch (IllegalAccessException e) {
            throw new SQLException(
                "Cannot set " + prop.getName() + ": " + e.getMessage());

        } catch (InvocationTargetException e) {
            throw new SQLException(
                "Cannot set " + prop.getName() + ": " + e.getMessage());
        }
    }

    /**
     * ResultSet.getObject() returns an Integer object for an INT column.  The
     * setter method for the property might take an Integer or a primitive int.
     * This method returns true if the value can be successfully passed into
     * the setter method.  Remember, Method.invoke() handles the unwrapping
     * of Integer into an int.
     *
     * @param value The value to be passed into the setter method.
     * @param type The setter's parameter type.
     * @return boolean True if the value is compatible.
     */
    private boolean isCompatibleType(Object value, Class type) {
        // Do object check first, then primitives
        if (value == null || type.isInstance(value)) {
            return true;

        } else if (type.equals(Long.TYPE) && Long.class.isInstance(value)) {
            return true;

        } else if (type.equals(Float.TYPE) && Float.class.isInstance(value)) {
            return true;

        } else if (type.equals(Short.TYPE) && Short.class.isInstance(value)) {
            return true;

        } else if (type.equals(Byte.TYPE) && Byte.class.isInstance(value)) {
            return true;

        } else if (
            type.equals(Character.TYPE) && Character.class.isInstance(value)) {
            return true;

        } else if (
            type.equals(Boolean.TYPE) && Boolean.class.isInstance(value)) {
            return true;

        } else {
            return false;
        }

    }

    /**
     * Returns a new instance of the given Class.
     *
     * @param c The Class to create an object from.
     * @return A newly created object of the Class.
     * @throws SQLException if creation failed.
     */
    private Object newInstance(Class c) throws SQLException {
        try {
            return c.newInstance();

        } catch (InstantiationException e) {
            throw new SQLException(
                "Cannot create " + c.getName() + ": " + e.getMessage());

        } catch (IllegalAccessException e) {
            throw new SQLException(
                "Cannot create " + c.getName() + ": " + e.getMessage());
        }
    }

    /**
     * Returns a PropertyDescriptor[] for the given Class.
     *
     * @param c The Class to retrieve PropertyDescriptors for.
     * @return A PropertyDescriptor[] describing the Class.
     * @throws SQLException if introspection failed.
     */
    private PropertyDescriptor[] propertyDescriptors(Class c)
        throws SQLException {
        // Introspector caches BeanInfo classes for better performance
        BeanInfo beanInfo = null;
        try {
            beanInfo = Introspector.getBeanInfo(c);

        } catch (IntrospectionException e) {
            throw new SQLException(
                "Bean introspection failed: " + e.getMessage());
        }

        return beanInfo.getPropertyDescriptors();
    }

    /**
     * A Map that converts all keys to lowercase Strings for case insensitive
     * lookups.  This is needed for the toMap() implementation because
     * databases don't consistenly handle the casing of column names.
     */
    private static class CaseInsensitiveHashMap extends HashMap {
        /**
         * Logger for this class
         */
        private static final Logger logger = Logger
                .getLogger(CaseInsensitiveHashMap.class);

        /**
         * @see java.util.Map#containsKey(java.lang.Object)
         */
        public boolean containsKey(Object key) {
            return super.containsKey(key.toString().toLowerCase());
        }

        /**
         * @see java.util.Map#get(java.lang.Object)
         */
        public Object get(Object key) {
            return super.get(key.toString().toLowerCase());
        }

        /**
         * @see java.util.Map#put(java.lang.Object, java.lang.Object)
         */
        public Object put(Object key, Object value) {
            return super.put(key.toString().toLowerCase(), value);
        }

        /**
         * @see java.util.Map#putAll(java.util.Map)
         */
        public void putAll(Map m) {
            Iterator iter = m.keySet().iterator();
            while (iter.hasNext()) {
                Object key = iter.next();
                Object value = m.get(key);
                this.put(key, value);
            }
        }

        /**
         * @see java.util.Map#remove(java.lang.ObjecT)
         */
        public Object remove(Object key) {
            return super.remove(key.toString().toLowerCase());
        }
    }

}

Posted by tornado
|

jetty site

JAVA/JSP_Servlet 2005. 10. 12. 11:13
Posted by tornado
|
JAVA IDE
 Download News & Updates
 Support Contact Us
 FAQ Tutorials and Demos
 Search Member Services
  

MyEclipse XDoclet Web Development Tutorial

Eric Glass, Independent Consultant
karicg@worldnet.att.net

 

Overview

This tutorial provides a detailed example for using the XDoclet support within MyEclipse to speed web development through the use of attribute-oriented programming. Using XDoclet greatly reduces development time since it automates the generation of deployment descriptors and other support code. This tutorial is based on the concepts that are explained more fully in the IBM developerWorks article Enhance J2EE component reuse with XDoclets by Rick Hightower. If you're new to XDoclet, reading the developerWorks tutorial is highly recommended to provide you the background information needed to fully understand what is described here. Additionally, once you've read how to do it the "hard way" you'll really appreciate the integrated facilities provided by MyEclipse as we develop a sample web application composed of an HTML page, a servlet, a JSP, and a custom taglib.

   
   

Steps to Easy Web Development

This section will take you through the step-by-step process of web project creation, XDoclet configuration, deployment descriptor generation, and Tomcat 5 deployment.

1) Create a New Web Project

  1. Change to or open the MyEclipse Perspective (Window > Open Perspecitve > Other... > MyEclipse) .
  2. Select File > New... > Project > J2EE > Web Module Project, then press the Next button.
  3. For the project name use MyXDocletWeb and for the context root specify /MyXDocletWeb, as shown in Figure 1 below, and press the Finish button.  The resulting project structure will be the same as that shown in Figure 2.

    Creating the Web Project
    Figure 1. Creating the Web Project

    Web Project Layout
    Figure 2. Web Project Layout

2) Create a Servlet

  1. Select the project MyXDocletWeb in the Package Explorer
  2. Select File > New... > Servlet,
  3. Populate it with the package name com.myeclipse.tutorial.servlet and class name BasicServlet as shown in Figure 3, then press the Next button.

    Creating the Servlet
    Figure 3. Creating the Servlet

  4. When the second page of the servlet wizard is displayed, deselect the checkbox labeled Generate/Map web.xml File and select the finish button as shown in Figure 4.  We don't need to have the wizard map the web.xml file since we'll be generating it based on our XDoclet settings later.

    Creating the Servlet - Page 2
    Figure 4. Creating the Servlet - Page 2

  5. After the servlet is generated, it will be opened in the Java editor. Replace the generated source code completely with the following contents and save the file.

/*
 * BasicServlet.java
 * Created on Aug 7, 2003
 */
package com.myeclipse.tutorial.servlet;

import java.io.IOException;
import java.io.PrintWriter;

import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

/**
 * Basic Servlet Using XDoclet.
 *
 * @author Administrator
 * @version 1.0, Aug 7, 2003
 *
 * @web.servlet name = "BasicServlet"
 *              display-name = "Basic Servlet"
 *              load-on-startup = "1"
 * @web.servlet-init-param name = "hi"
 *                         value = "${basic.servlet.hi}"
 * @web.servlet-init-param name = "bye"
 *                         value = "${basic.servlet.bye}"
 * @web.servlet-mapping url-pattern = "/Basic/*"
 * @web.servlet-mapping url-pattern = "*.Basic"
 * @web.servlet-mapping url-pattern = "/BasicServlet"
 * @web.resource-ref description = "JDBC resource"
 *                   name = "jdbc/mydb"
 *                   type = "javax.sql.DataSource"
 *                   auth = "Container"
 */
public class BasicServlet extends HttpServlet {
    /**
     * Constructor of the object.
     */
    public BasicServlet() {
        super();
    }

    /**
     * Destruction of the servlet.
     */
    public void destroy() {
        super.destroy(); // Just puts "destroy" string in log
    }

    /**
     * The doGet method of the servlet.
     *
     * @param request the request send by the client to the server
     * @param response the response send by the server to the client
     *
     * @throws ServletException if an error occurred
     * @throws IOException if an error occurred
     */
    public void doGet(HttpServletRequest request, HttpServletResponse response)
        throws ServletException, IOException {
        processRequest(request, response);
    }

    /**
     * The doPost method of the servlet.
     *
     * @param request the request send by the client to the server
     * @param response the response send by the server to the client
     *
     * @throws ServletException if an error occurred
     * @throws IOException if an error occurred
     */
    public void doPost(HttpServletRequest request, HttpServletResponse response)
        throws ServletException, IOException {
        processRequest(request, response);
    }

    /**
     * Returns information about the servlet.
     *
     * @return String information about this servlet
     */
    public String getServletInfo() {
        return "Basic Servlet Using XDoclet";
    }

    /**
     * Initialization of the servlet.
     *
     * @throws ServletException if an error occurred
     */
    public void init() throws ServletException {
        // Put your code here
    }

    /**
     * Initialization of the servlet with the servlet's configuration.
     *
     * @param config the servlet's configuration
     *
     * @throws ServletException if an error occurred
     */
    public void init(ServletConfig config) throws ServletException {
        super.init(config);
    }

    /**
     * Processes requests for both HTTP GET and POST methods.
     *
     * @param request servlet request
     * @param response servlet response
     *
     * @throws ServletException if an error occurred
     * @throws java.io.IOException if an I/O error occurred
     */
    protected void processRequest(HttpServletRequest request,
        HttpServletResponse response) throws ServletException, IOException {
        ServletConfig config = this.getServletConfig();
        String hi = config.getInitParameter("hi");
        String bye = config.getInitParameter("bye");

        try {
            response.setContentType("text/html");

            PrintWriter out = response.getWriter();
            out.println("<html>");
            out.println("<head>");
            out.println("<title>Basic Servlet</title>");
            out.println("</head>");
            out.println("<body>");
            out.println("<h1>hi:  " + hi + "</h1>");
            out.println("<h1>bye:  " + bye + "</h1>");
            out.println("</body>");
            out.println("</html>");
            out.close();
        } catch (Exception e) {
            throw new ServletException(e);
        }
    }
}

Explanation of XDoclet Tags Used in the Servlet
The example tag illustrates the use of several helpful XDoclet tags. The tags and what they generate are displayed in the table below.
XDoclet TagGenerated Code
@web.servlet name = "BasicServlet"     display-name = "Basic Servlet"     load-on-startup = "1" Generated into the web.xml:

<servlet>
   <servlet-name>BasicServlet</servlet-name>
   <display-name>Basic Servlet</display-name>
   <servlet-class>com.myeclipse.tutorial.servlet.BasicServlet</servlet-class>
   ...
   <load-on-startup>1</load-on-startup>
</servlet>
@web.servlet-init-param name = "hi"
    value = "${basic.servlet.hi}"
@web.servlet-init-param name = "bye"
    value = "${basic.servlet.bye}"
Generated into the web.xml when Ant properties are set to: (basic.servlet.hi = Ant is cool!) and (basic.servlet.bye = XDoclet Rocks!):

<servlet>
   ...
   <init-param>
      <param-name>hi</param-name>
      <param-value>Ant is cool!</param-value>
   </init-param>
   <init-param>
      <param-name>bye</param-name>
      <param-value>XDoclet Rocks!</param-value>
   </init-param>
   ...
</servlet>
@web.servlet-mapping url-pattern = "/Basic/*"
@web.servlet-mapping url-pattern = "*.Basic"
@web.servlet-mapping url-pattern = "/BasicServlet"
Generated into web.xml:

<servlet-mapping>
   <servlet-name>BasicServlet</servlet-name>
   <url-pattern>/Basic/*</url-pattern>
</servlet-mapping>
<servlet-mapping>
   <servlet-name>BasicServlet</servlet-name>
   <url-pattern>*.Basic</url-pattern>
</servlet-mapping>
<servlet-mapping>
   <servlet-name>BasicServlet</servlet-name>
   <url-pattern>/BasicServlet</url-pattern>
</servlet-mapping>
@web.resource-ref
    description = "JDBC resource"
    name = "jdbc/mydb"
    type = "javax.sql.DataSource"
    auth = "Container"
Generated into web.xml:

<resource-ref>
   <description>JDBC resource</description>
   <res-ref-name>jdbc/mydb</res-ref-name>
   <res-type>javax.sql.DataSource</res-type>
   <res-auth>Container</res-auth>
</resource-ref>

3) Create a Custom Tag Class

  1. Select the project MyXDocletWeb in the Package Explorer
  2. Select File > New... > Class,
  3. Populate it with the package name com.myeclipse.tutorial.customtag, class name BasicTag, and subclass javax.servlet.jsp.tagext.TagSupport as shown in Figure 5, then press the Finish button.

    Creating a Custom Tag
    Figure 5. Creating a Custom Tag

  4. After the tag is generated, it will be opened in the Java editor. Replace the generated source code completely with the following contents and save the file.

/*
 * BasicTag.java
 */
package com.myeclipse.tutorial.customtag;

import javax.servlet.jsp.JspException;
import javax.servlet.jsp.tagext.TagSupport;

/**
 * Basic Custom Tag Using XDoclet.
 *
 * @jsp.tag name="BasicTag"
 * @jsp.variable name-given="currentIter"
 *               class="java.lang.Integer"
 *               scope="NESTED"
 *               declare="true"
 * @jsp.variable name-given="atBegin"
 *               class="java.lang.Integer"
 *               scope="AT_BEGIN"
 *               declare="true"
 * @jsp.variable name-given="atEnd"
 *               class="java.lang.Integer"
 *               scope="AT_END"
 *               declare="true"
 *
 */
public class BasicTag extends TagSupport {
    /** Holds value of property includePage. */
    private boolean includePage = false;

    /** Holds value of property includeBody. */
    private boolean includeBody = false;

    /** Holds value of property iterate. */
    private int iterate = 0;

    /**
     * Creates a new BasicTag object.
     */
    public BasicTag() {
        super();
    }

    /**
     * @see javax.servlet.jsp.tagext.TagSupport#doStartTag()
     */
    public int doStartTag() throws JspException {
        pageContext.setAttribute("currentIter", new Integer(iterate));
        pageContext.setAttribute("atBegin", new Integer(0));

        return includeBody ? EVAL_BODY_INCLUDE : SKIP_BODY;
    }

    /**
     * @see javax.servlet.jsp.tagext.TagSupport#doEndTag()
     */
    public int doEndTag() throws JspException {
        pageContext.setAttribute("atEnd", new Integer(iterate));

        return includePage ? EVAL_PAGE : SKIP_PAGE;
    }

    /**
     * @see javax.servlet.jsp.tagext.TagSupport#doAfterBody()
     */
    public int doAfterBody() throws JspException {
        iterate -= 1;
        pageContext.setAttribute("currentIter", new Integer(iterate));

        if (iterate <= 0) {
            return SKIP_BODY;
        } else {
            return EVAL_BODY_AGAIN;
        }
    }

    /**
     * Getter for property includePage.
     *
     * @return Value of property includePage.
     *
     * @jsp.attribute required="true"
     *                rtexprvalue="true"
     *                description="The includePage attribute"
     */
    public boolean isIncludePage() {
        return includePage;
    }

    /**
     * Setter for property includePage.
     *
     * @param includePage New value of property includePage.
     */
    public void setIncludePage(boolean includePage) {
        this.includePage = includePage;
    }

    /**
     * Getter for property includeBody.
     *
     * @return Value of property includeBody.
     *
     * @jsp.attribute required="true"
     *                rtexprvalue="true"
     *                description="The includeBody attribute"
     */
    public boolean isIncludeBody() {
        return includeBody;
    }

    /**
     * Setter for property includeBody.
     *
     * @param includeBody New value of property includeBody.
     */
    public void setIncludeBody(boolean includeBody) {
        this.includeBody = includeBody;
    }

    /**
     * Getter for property iterate.
     *
     * @return Value of property iterate.
     *
     * @jsp.attribute required="true"
     *                rtexprvalue="true"
     *                description="The iterate attribute"
     */
    public int getIterate() {
        return iterate;
    }

    /**
     * Setter for property iterate.
     *
     * @param iterate New value of property iterate.
     */
    public void setIterate(int iterate) {
        this.iterate = iterate;
    }
}

Explanation of XDoclet Tags Used in the Custom Tag
The example tag  illustrates the use of several helpful XDoclet tags. The tags and what they generate are displayed in the table below.
XDoclet TagGenerated Code
@jsp.tag name="BasicTag" Generated into the taglib's .tld file:

<tag>
   <name>BasicTag</name>
   <tag-class>
       com.myeclipse.tutorial.customtag.BasicTag
   </tag-class>
   ...
</tag>
@jsp.variable 
    name-given="currentIter"
    class="java.lang.Integer"
    scope="NESTED"
    declare="true"
@jsp.variable 
    name-given="atBegin"
    class="java.lang.Integer"
    scope="AT_BEGIN"
    declare="true"
@jsp.variable 
    name-given="atEnd"
    class="java.lang.Integer"
    scope="AT_END"
    declare="true"
Generated into the taglib's .tld file:

<tag>
   ...
   <variable>
      <name-given>currentIter</name-given>
      <variable-class>java.lang.Integer</variable-class>
  <declare>true</declare>
      <scope>NESTED</scope>
   </variable>
   <variable>
      <name-given>atBegin</name-given>
      <variable-class>java.lang.Integer</variable-class>
  <declare>true</declare>
      <scope>AT_BEGIN</scope>
   </variable>
   <variable>
      <name-given>atEnd</name-given>
      <variable-class>java.lang.Integer</variable-class>
  <declare>true</declare>
      <scope>AT_END</scope>
   </variable>
   ...
</tag>
@jsp.attribute required="true"
    rtexprvalue="true"
    description="The includePage attribute"
@jsp.attribute required="true"
    rtexprvalue="true"
    description="The includeBody attribute"
@jsp.attribute required="true"
    rtexprvalue="true"
    description="The iterate attribute"
Generated into the taglib's .tld file:

<tag>
   ...
   <attribute>
      <name>includePage</name>
      <required>true</required>
      <rtexprvalue>true</rtexprvalue>
      <description><![CDATA[The includePage attribute]]></description>
   </attribute>
   <attribute>
      <name>includeBody</name>
      <required>true</required>
      <rtexprvalue>true</rtexprvalue>
      <description><![CDATA[The includeBody attribute]]></description>
   </attribute>
   <attribute>
      <name>iterate</name>
      <required>true</required>
      <rtexprvalue>true</rtexprvalue>
      <description><![CDATA[The iterate attribute]]></description>
   </attribute>
</tag>

4) Add a Folder Called 'merge' to the Project

  1. Select the project MyXDocletWeb in the Package Explorer
  2. Select File > New... > Folder,
  3. Populate it with the name merge, as shown in Figure 6, then press the Finish button.

    Creating the Merge Folder
    Figure 6. Creating the Merge Folder

5) Create the taglibs.xml file

  1. Select the folder MyXDocletWeb/merge in the Package Explorer
  2. Select File > New... > XML,
  3. Populate it with the name taglibs.xml, as shown in Figure 7, then press the Finish button.

    Creating the taglibs.xml file
    Figure 7. Creating the taglibs.xml file

Once taglibs.xml has been created and opened in the XML editor, completely replace the contents with the following:

<taglib>
   <taglib-uri>/mytaglib</taglib-uri>
   <taglib-location>/WEB-INF/mytaglib.tld</taglib-location>
</taglib>

6) Create the welcomefiles.xml file

  1. Select the folder MyXDocletWeb/merge in the Package Explorer
  2. Select File > New... > XML,
  3. Populate it with the name welcomefiles.xml then press the Finish button as you did for taglibs.xml.
Once welcomefiles.xml has been created and opened in the XML editor, completely replace the contents with the following:

<welcome-file-list>
   <welcome-file>index.jsp</welcome-file>
   <welcome-file>index.html</welcome-file>
</welcome-file-list>

7) Create the xdoclet-build.properties file

  1. Select the folder MyXDocletWeb in the Package Explorer
  2. Select File > New... > File,
  3. Populate it with the name xdoclet-build.properties, as shown in Figure 8, then press the Finish button.

    Creating the xdoclet-build.properties file
    Figure 8. Creating the xdoclet-build.properties file

    Once xdoclet-build.properties has been created and opened in the Text editor, completely replace the contents with the following:

basic.servlet.hi = MyEclipse Rocks!
basic.servlet.bye = Feel the power of XDoclet!

8) Configure the Project for XDoclet Usage

  1. Right-click on the folder MyXDocletWeb in the Package Explorer
  2. Select Properties > XDoclet Configurations
  3. Right-click and select Add Standard as shown in Figure 9.
  4. Select Standard Web from the resulting dialog box. The resulting configuration is shown in Figure 10.

    Adding a Standard XDoclet Configuration
    Figure 9. Adding a Standard XDoclet Configuration

    Adding Standard Web Configuration
    Figure 10. Adding Standard Web Configuration

  5. Select deploymentdescriptor under the webdoclet entry and select and set the following values as shown in the table below and in Figure 11.
    PropertyValue
    Servletspec2.3
    destDirWebRoot/WEB-INF
    displayNameXDoclet Web Tutorial
    mergeDirmerge

    Deployment Descriptor Settings
    Figure 11. Deployment Descriptor Settings

  6. Select jsptaglib under the webdoclet entry and select and set the following values as shown in the table below and in Figure 12.
    PropertyValue
    Jspversion1.2
    destDirWebRoot/WEB-INF
    destinationFilemytaglib.tld
    shortnamebasic

    JSP Taglib Settings
    Figure 12. JSP Taglib Settings

  7. Select the OK button at the bottom of the Properties dialog to save the XDoclet Configurations. This will generate the file xdoclet-build.xml into the project folder.

9) Setting XDoclet Ant Properties

  1. Right-click on the generated xdoclet-build.xml file in the Package Explorer and select Run Ant....
  2. On the dialg, titled MyXDocletWeb xdoclet-build.xml, select the Properties tab.
  3. Select the Add... button next to the Property files: list
  4. Browse to the project folder select xdoclet-build.properties.
  5. The resultant configuration will look like that in Figure 13.

    Adding Xdoclet Properties File
    Figure 13. Adding the Properties File

  6. Select the Refresh tab on the dialog and ensure that Refresh references after running tool is checked.

    Checking the Refresh Settings
    Figure 14. Checking the Refresh Settings

  7. Select the Apply button to save the changes.
  8. Select the Run button to process the XDoclet JavaDoc statements in the Java source with the Ant webdoclet task.
  9. After XDoclet runs, the files web.xml and mytaglib.tld will have been added to the WEB-INF directory as shown in Figure 15 below.

    Generated Files
    Figure 14. Generated Files

10) Adding a JSP Page

  1. Select the project MyXDocletWeb in the Package Explorer
  2. Right-click and select New... > JSP,
  3. Populate the wizard page with the file name TestJSP.jsp, as shown in Figure 15, then press the Finish button.

    Creating a JSP
    Figure 15. Creating a JSP

  4. After the JSP is generated, it will be opened in the JSP editor. Replace the generated source code completely with the following contents and save the file.

<%@ page language="java" %>
<%@ taglib uri="/mytaglib" prefix="mytag" %>

<html>
  <head>
    <title>I am a happy JSP page. Yeah!</title>
  </head>
  <body>
    <mytag:BasicTag includePage="true" includeBody="true" iterate="3">
      Current iteration is <%=currentIter%> <br/>
    </mytag:BasicTag>
  </body>
</html>

10) Adding an HTML Page

  1. Select the project MyXDocletWeb in the Package Explorer
  2. Right-click and select New... > HTML,
  3. Populate the wizard page with the file name index.html, as shown in Figure 16, then press the Finish button.

    Creating an HTML Page
    Figure 16. Creating an HTML Page

  4. After the HTML page is generated, it will be opened in the HTML editor. Replace the generated source code completely with the following contents and save the file.

<html>
  <head>
    <title>XDoclet Web Tutorial</title>
  </head>
  <body>
    <br />
    <blockquote>
      <h3>XDoclet Web Tutorial</h3>
      <ul>
        <li><a href="TestJSP.jsp">Test Basic JSP Custom Tag</a></li>
        <li><a href="BasicServlet">Test Basic Servlet</a></li>
      </ul>
    </blockquote>
  </body>
</html>

11) Verify Project

The project is now complete. To verify that the structure is complete, please compare your project to the one shown in Figure 17.

Final Project Structure
Figure 17. Final Project Structure

12) Deploy the Project and Test

  1. Right-click on the MyXDocletWeb project and select MyEclipse > Add and Remove Project Deployments as shown in Figure 18.

    Opening the Deployment Dialog
    Figure 18. Opening the Deployment Dialog

  2. Select the Add button as shown in Figure 19.

    Adding a Deployment
    Figure 19. Adding a Deployment

  3. Select whatever server you've got configured as an exploded archive as shown in Figure 20.

    Picking the Application Server
    Figure 20. Picking a Server

  4. Select the OK button as shown in Figure 21.

    Completing Deployment
    Figure 21. Completing Deployment

  5. Start the server as shown in Figure 22.

    Starting the Application Server
    Figure 22. Starting the Server

  6. Open a browser and test the application. The results are shown in Figure 23.

    Testing the Application 1 Testing the Application 2 Testing the Application 3
    Figure 23. Testing the Application

    Conclusion

    This has been an introduction to XDoclet use for web applications within MyEclipse. Although the example application was a simple one, it was a complete application that included an HTML page, a JSP page, an XDoclet-based Servlet, and and XDoclet-based Tag library. Now that you know how the basics work, get out there and start simplifying your web development with MyEclipse and XDoclet!
 
Genuitec Offers a broad range of consulting services including an Eclipse practice for plugin development, training and customization. Genuitec Eclipse services cover all aspects of the Eclipse framwork and include:
Genuitec Solutions and Services
  • Custom Plugin Development for Eclipse-Based Applications
  • Eclipse Development Courses and Workshops
  • Custom Widget Development and SWT/JFaces Extensions
  • Business Desktop Application or Suite Development
  • Productivity Tools Development and Branding
  • J2EE Consulting and Out-sourcing

 
MyEclipse mission is to deliveran affordable and a true end-to-end seamless development environment for Web, J2EE, XML, JSP, Struts, and application server integration.
MyEclipse Features
  • Web Development Tools
    • Smart editors - JSP, HTML, Struts, XML, CSS, and J2EE deployment descriptors
    • XML editing
    • Struts Support
    • XDoclet Support
  • Productivity Wizards
    • Creation of Web, EAR and EJB projects.
    • Java Project to Web Project enablements.
    • EJB Wizards
    • Automated deployment.
  • Application Server Integration
    • 20 application server connectors
    • Integrated controls
    • Full hot swap debugging
 
  
   
   

'JAVA > JSP_Servlet' 카테고리의 다른 글

[펌] apache.common.DbUtil & Oracle 용 RowProcess  (0) 2005.10.17
jetty site  (0) 2005.10.12
[펌] 웹개발문서모음 - iHelpers  (0) 2005.08.24
[openlaszlo] 아래에 푸터 붙어있을때...  (0) 2005.07.05
jwebunit  (0) 2005.06.03
Posted by tornado
|

출처 : http://www.ihelpers.co.kr/webdevdoc/index.php

 

  웹개발 문서 모음

웹사이트 개발시에 필요한 문서 목록입니다.
해당 문서목록의 의견,문제점,궁금하신 점은 Q&A게시판에 올려주십시요.
공유를 원하는 유용한 웹개발문서를 받고 있습니다. 많이 많이 여기로 보내주세요.(다양한 업종의 스토리보드와 제안서 강추)

Q : 파일 다운로드시 한글파일의 경우 다운로드 되지 않거나 글자가 깨집니다.
A : 익스플로러 "도구 > 인터넷 옵션 > 고급 "에서 고급 UTF8옵션 해제하시면 됩니다.

* 버전 : v2.0
* 최종수정일자 : 2005.07.08 10:30:22

'JAVA > JSP_Servlet' 카테고리의 다른 글

jetty site  (0) 2005.10.12
[펌] myeclipse에서 xdoclet 설명  (0) 2005.10.04
[openlaszlo] 아래에 푸터 붙어있을때...  (0) 2005.07.05
jwebunit  (0) 2005.06.03
OSCache / Quartz ... 등등 좋은것들..  (0) 2005.06.03
Posted by tornado
|

요청 쿼리에 ?lzt=html 를 붙이니까 없어짐..

 

http://192.168.1.53:8080/lps-3.0/my-apps/hello1.lzx?lzt=html

http://192.168.1.53:8080/lps-3.0/my-apps/hello1.lzx?lzt=html-object

 

이런식으로 호출함..

 

라즐로 이용해서 포토 게시판 만들면 끝장이겠는걸~

'JAVA > JSP_Servlet' 카테고리의 다른 글

[펌] myeclipse에서 xdoclet 설명  (0) 2005.10.04
[펌] 웹개발문서모음 - iHelpers  (0) 2005.08.24
jwebunit  (0) 2005.06.03
OSCache / Quartz ... 등등 좋은것들..  (0) 2005.06.03
[펌] Opensource Web Grid  (0) 2005.05.30
Posted by tornado
|

jwebunit

JAVA/JSP_Servlet 2005. 6. 3. 11:24
Posted by tornado
|

http://www.opensymphony.com/

 

볼꺼는 많고... 음주가무는 끊이지 않고... 고민...

 

'JAVA > JSP_Servlet' 카테고리의 다른 글

[openlaszlo] 아래에 푸터 붙어있을때...  (0) 2005.07.05
jwebunit  (0) 2005.06.03
[펌] Opensource Web Grid  (0) 2005.05.30
[펌] TD 를 에디트 하기 ,,,  (0) 2005.05.13
Tyrex 이용한 jndi 이용방법..  (2) 2005.05.10
Posted by tornado
|

출처 : LoveLazur

 

Opensource Web Grid

1) 대략 javascript아니면 DHTML [javascript라면 이것도 OK]
http://www.activewidgets.com/documentation/tutorial/grid/data-jsarray.htm

하지만 이렇게 작성도 가능함 [tag lib version]
http://www.activewidgets.com/messages/1694-8.htm



2) http://www.codeproject.com/useritems/DBGrid.asp
순수 jsp타입에서 접근하기엔 이게 좋아보인다.

2-2) sand box에서는 이런식으로 component화 되어있음 tablib임.
http://jakarta.apache.org/taglibs/sandbox/doc/datagrid-doc/index.html


2-3) 개인적으로 이게 좋은데... 단점이 없지 않음
http://displaytag.sourceforge.net/

 

 

------ 좋다 ... 이중에 맨 마지막거. -----

'JAVA > JSP_Servlet' 카테고리의 다른 글

jwebunit  (0) 2005.06.03
OSCache / Quartz ... 등등 좋은것들..  (0) 2005.06.03
[펌] TD 를 에디트 하기 ,,,  (0) 2005.05.13
Tyrex 이용한 jndi 이용방법..  (2) 2005.05.10
에혀... 문서를 자세히 봐야해...  (0) 2005.05.03
Posted by tornado
|

http://www.servletsuite.com/tips/cells.htm

 

헐.. TD 를 에디트 하다뉘.. 머리 조타...

 

'JAVA > JSP_Servlet' 카테고리의 다른 글

OSCache / Quartz ... 등등 좋은것들..  (0) 2005.06.03
[펌] Opensource Web Grid  (0) 2005.05.30
Tyrex 이용한 jndi 이용방법..  (2) 2005.05.10
에혀... 문서를 자세히 봐야해...  (0) 2005.05.03
이클립스플러긴  (0) 2005.04.25
Posted by tornado
|

proxool  커넥션 풀 소스를 보다가... jndi 를 이용하는 다른 방법이 보이길래 끄적여 봄...

 

http://tyrex.sourceforge.net/  <-- 요기에 보면 여러가지 프로젝들이 보인다..

그중... 설명은 안나온것 같은데.. 톰캣 처럼.. jndi 를 불편하게 사용해야 할 경우(반드시 server.xml 에 적어줘야 하며, 서버 재시작 필요함..  readonly 환경이라 그렇다고 하넹..)

tyrex-naming 을 이용하면 된다....

현재 버젼은 1.0.2 인가 그렇고..

테스트 한 패키지는 1.0.1 이며 naming 관련 부분만 이용했다.

 

먼저.. 간단한 자바 빈 클래스.. 이 녀석을 jndi 에 등록할것임..

 

package com.javarush.test;


public class TestBean {
   
    private String print;
   
    public TestBean(){ }

    public String getPrint() {
        return print;
    }

    public void setPrint(String print) {
        this.print = print;
    }   
}

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

초기화 시켜줄 서블릿...

web.xml 에 당근 등록해야 하고... <load-on-startup>3<load-on-startup>  으로 지정했다.

import java.io.IOException;
import java.util.Hashtable;

import javax.naming.Context;
import javax.naming.InitialContext;
import javax.naming.spi.NamingManager;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import tyrex.naming.MemoryContextFactory;
import com.javarush.test.TestBean;

public class InittServlet extends HttpServlet {

    public void destroy() {
        super.destroy();
    }


    public void doGet(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {

    }


    public void doPost(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {

    }

    public void init() throws ServletException {
        String alias = "myAliasName";
        String jndiName = "my/testbean";
       
        try{
           
            TestBean bean = new TestBean();
            bean.setPrint("HIHI");
       
         Hashtable env = new Hashtable();
         env.put(Context.INITIAL_CONTEXT_FACTORY, MemoryContextFactory.class.getName());
         env.put(Context.URL_PKG_PREFIXES, "tyrex.naming");
         env.put(Context.PROVIDER_URL, alias);
         Context context = new InitialContext(env);
         context.createSubcontext("my");
         context.bind(jndiName, NamingManager.getObjectInstance(bean, null, null, null));
         context.close();
       
        }catch(Exception e){
            e.printStackTrace();
        }
    }
}

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

Test 용 JSP

<%@ page contentType="text/html; charset=euc-kr"
 import = "javax.naming.*, java.util.*, com.javarush.test.*, tyrex.naming.*"

%><%

        String alias = "myAliasName";
        String jndiName = "my/testbean";
        Hashtable env = new Hashtable();
        env.put(Context.INITIAL_CONTEXT_FACTORY, MemoryContextFactory.class.getName());
        env.put(Context.URL_PKG_PREFIXES, "tyrex.naming");
        env.put(Context.PROVIDER_URL, alias);

        Context context = new InitialContext(env);
        TestBean bean = (TestBean) context.lookup(jndiName);
        context.close();
       
        out.print(bean.getPrint());

%>

 

 

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

첨부된 tyrex-1.0.1_naming.jar 파일은 WEB-INF/lib 에 두고..

실행해 보면

브라우저에 HIHI 가 출력될 것임....

 

 

 

 

 

 

'JAVA > JSP_Servlet' 카테고리의 다른 글

[펌] Opensource Web Grid  (0) 2005.05.30
[펌] TD 를 에디트 하기 ,,,  (0) 2005.05.13
에혀... 문서를 자세히 봐야해...  (0) 2005.05.03
이클립스플러긴  (0) 2005.04.25
자바싱글사인온 .. 오오옷~! 함 해봐야쥐  (0) 2005.04.13
Posted by tornado
|
<Host name="www.mycompany.com" ...>  ...  <Alias>mycompany.com</Alias>  ...</Host>

 

톰캣 설정 중에 Host name Alias 땜쉬.. 잠깐 헤맸네 ㅎㅎ

Posted by tornado
|

http://www.junginger.biz/eclipse/index.html

 

메모리 보는것과 RSS 뷰~ 

Posted by tornado
|

'JAVA > JSP_Servlet' 카테고리의 다른 글

에혀... 문서를 자세히 봐야해...  (0) 2005.05.03
이클립스플러긴  (0) 2005.04.25
[펌] lucene index browser 오옷~ 00  (2) 2005.03.31
[링크]proxool.. 편리한 풀...  (0) 2005.03.22
[링크]자바로 만든 블로그  (2) 2005.02.11
Posted by tornado
|
Lucene for ASP.NET) 인덱스데이터 브라우저 Luke 소개.. by udanax

  E-mail : udanax@msn.com
  Read : 53

  Date : 2005-03-28(월) 11:47:31
  IP : 211.195.195.xxx

Lucene 인덱스 데이터 브라우저군요.
You can download the source code here (106kB): luke.zip
자동 설치 URL은 http://www.getopt.org/luke/luke.jnlp 입니다.

Posted by tornado
|

http://proxool.sourceforge.net/index.html

설정 엄청 편리... 관리 서블릿 제공 ... 무지 단순 -_-;

 

 

Posted by tornado
|

http://wiki.blojsom.com/wiki/display/blojsom

 

오... 벨로시티로 스킨을 .....

 

 

http://b2evolution.net/  <--- PHP 로 만든 블로그...

 

asp 로 만든건 없나??

'JAVA > JSP_Servlet' 카테고리의 다른 글

[펌] lucene index browser 오옷~ 00  (2) 2005.03.31
[링크]proxool.. 편리한 풀...  (0) 2005.03.22
간단한 trackback ping 보내는 JSP ..  (0) 2005.02.04
[펌] Trackback CGI 설계  (0) 2005.02.03
[펌] 검색엔진 만들기  (0) 2005.01.21
Posted by tornado
|

심심해서(?) 잠깐 만들어봄 ㅡㅡ

PHP 는 금새 했는데 JSP 는 HttpURLConnection 에서 잠깐 헤맴 ㅜㅜ

 

<%@ page contentType="text/xml; charset=euc-kr" %>
<%@ page import="java.util.*, java.net.*, java.io.*, org.jdom.*, org.jdom.input.*" %>
<%


 String receipt = "http://blog.empas.com/yheesung/tb/6327649";


 String url = "http://yheesung.egloos.com/";


 String title = URLEncoder.encode("트랙백연습", "euc-kr");


 String blog_name = URLEncoder.encode("tornado Blog", "euc-kr");


 String excerpt = URLEncoder.encode("이 글의 내용은 어짜구.. 저짜구......", "euc-kr");


 URL receiptURL = new URL(receipt);

 

 HttpURLConnection conn = (HttpURLConnection)receiptURL.openConnection();

 

 conn.setDoOutput(true);

 

 //==================================================================
 // 특별히 Header 정보를 셋팅할 필요는 없다.
 // 보내는 부분에서는 아무 문제가 없었다.
 // Test : 이글루, 네이버, 엠파스
 //==================================================================

 conn.setRequestMethod("GET");
 
 //conn.setRequestMethod("POST");

 PrintWriter pw = new PrintWriter(conn.getOutputStream());

 pw.write("url="+url);

 pw.write("&title=" + title);

 pw.write("&blog_name=" + blog_name);

 pw.write("&excerpt=" + excerpt);

 pw.flush();

 pw.close();

 BufferedReader reader = new BufferedReader(new InputStreamReader(conn.getInputStream()));

 //==================================================================
 //
 // 헐... 왜 xml문서 앞에 공백을 넣어서리..... ㅡㅡ;;
 //
 //================================================================== 
 
 String txt = "";

 while( (txt = reader.readLine()) != null){

  if(txt.indexOf("<?xml") > -1){

   break;

  }

 }

 String message = null;

 String status = null;

 boolean errorFlag = true;


 SAXBuilder builder = new SAXBuilder();

 builder.setValidation(false);

 builder.setIgnoringElementContentWhitespace(true);
  
 Document document = builder.build(reader);

 Element rootElement = document.getRootElement();

 List list = rootElement.getChildren();

 for(int i = 0; i < list.size(); i++){

  Element _el = (Element)list.get(i);


  if("error".equalsIgnoreCase(_el.getName())){

   status = _el.getValue();

  }

  if("message".equalsIgnoreCase(_el.getName())){

   message = _el.getValue();

  }

 }


 
 if("1".equals(status)){

  out.println("트랙백 핑을 보내는 중 에러가 발생하였습니다!!");

  out.print("<BR>Error code : " + status);

  out.print("<BR>Error message : " + message);

 }else{

  out.println("트랙백 핑이 에러가 없이 전송되었습니다");

 }

 reader.close();

 conn.disconnect();

%>

'JAVA > JSP_Servlet' 카테고리의 다른 글

[링크]proxool.. 편리한 풀...  (0) 2005.03.22
[링크]자바로 만든 블로그  (2) 2005.02.11
[펌] Trackback CGI 설계  (0) 2005.02.03
[펌] 검색엔진 만들기  (0) 2005.01.21
[javamail] 제목 깨질때...  (0) 2005.01.18
Posted by tornado
|

이 문서는 Trackback CGI를 제작하기 원하는 이들을 위해 제공하는 도움서일 뿐입니다.
제로보드용 블로그 스킨 Paz에 트랙백 기능을 추가해달라는 요구들이 많더군요. 제가 직접 할까하고 손 댔다가, 역시 코딩은 만만치 않군요. ^^; 저보다 더 잘 코딩해주실 분들이 많이 계시고, 또 현재 준비중이신 것 같아서 제가 직접 하는 것보다는 그분들을 위한 CGI설계에 도움되는 쪽이 나을 것 같아 이 문서를 작성합니다.

트랙백의 flow
트랙백은 간단한 HTTP + XML의 형태로 구현가능합니다.
편의상 트랙백을 보내는 쪽을 sender, 받는 쪽을 receiver라고 칭하겠습니다.

sender.cgi에서 receiver.cgi로 HTTP를 통해 보내는 정보는 다음과 같습니다.

POST Method


title : 해당 엔트리의 타이틀
url : 해당 엔트리의 고유주소
excerpt : 해당 엔트리의 내용 요약(혹은 설명)
blog_name : 블로그의 이름

그렇다면 receiver는 POST로 넘어온 이 값을 DB나 TXT등으로 저장 후, 잘 받았다는 값을 sender쪽에 회신해주면 됩니다.

이상이 트랙백의 전부입니다. 너무 간단하다구요? ^^; 맞습니다.

조금 더 상세히 살펴보도록 하지요.

사용자가 트랙백을 보내고자 할 때 sender와 receiver가 해야할 일은 다음과 같습니다.

1)트랙백 메시지 준비하기.
receiver쪽에 전달할 트랙백 메시지를 준비합니다. 필요한 어트리뷰트는,
*title:현재 문서의 제목
*url:현재 문서의 고유주소
*excerpt:현재 문서 내용의 요약 혹은 설명 (255자 이하로 축약하는 것이 좋음. 본문 내용에서 적당히 끊으면 되겠죠?)
*receipt:받는 쪽의 트랙백수신 CGI 주소 (사용자가 텍스트 박스등에 입력하도록 해야겠지요?)

2)트랙백 전달
receipt로 title, url, excerpt의 값을 POST 메쏘드를 이용해 전달합니다. 그리고 회신이 돌아올 때까지 기다립니다.

3)트랙백 수신
receiver쪽에서는 receipt로 넘어온 값을 확인한 후, sender에게 회신을 보내줍니다.
다음은 php로 작성한 간단한 receiver.php입니다.

<?
$fh = fopen("./tb.log", 'w');
$results = print_r($_POST, true);
$id = $_GET['id'];
fwrite($fh, $id);
fwrite($fh, $results);
fclose($fh);
header("Content-Type: text/xml");
print "<?xml version=\"1.0\" encoding=\"iso-8859-1\"?>\n";
print "<response>\n";
print "<error>0</error>\n";
print "</response>\n";
?>


이것만으로도 충분히 트랙백을 수신할 수 있습니다. 물론 실제로 활용하기 위해서는 받은 트랙백값을 DB에 저장해야겠지요. 위의 코드에서 id는 수신하는쪽의 문서의 id를 말합니다. 각 개별문서마다 일일이 트랙백 수신 CGI를 만들어 줄 수 없기 때문에 공용 수신 CGI를 이용해야 할테고, 그러자면 송신측에서는 자기가 어느 문서를 향해 보내는지 알리기 위한 id가 필요하겠죠. 이 id는 트랙백 규격에는 포함되지 않으므로 GET method로 그냥 보내면 됩니다. receiver.php?id=123 이런 식으로 말이죠.

header()이하 부분은 정상적인 트랙백 메시지일 경우 잘받았다는 회신으로 Sender쪽에 Response해줍니다. php에서는 그냥 print로 출력해버리면 되지요. ^^; 만약 정상적인 트랙백 메시지가 아니라서 수신을 거부할 경우 error 항목을 0대신 1로 바꿔주면 됩니다.

4)트랙백 회신 확인
이번엔 sender쪽에서 Response 받은 문자열을 분석해서 error가 0인지, 1인지 확인하면 되겠죠. 그에 따라 적당한 메시지를 출력해주면 끝.

5)문서 출력시
일단 DB를 뒤져서 이 문서로 들어온 트랙백 데이터가 있는지 확인합니다. 데이터가 있으면 문서 중에 적당한 자리에 그 정보를 붙여 넣으면 됩니다.


6)트랙백 자동발견용 RDF 아이템 문서내 삽입.
트랙백을 걸 수 있게 준비가 되면, 트랙백을 걸어도 좋다는 메시지를 문서속에 삽입해야합니다.

<!--
<rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:trackback="http://madskills.com/public/xml/rss/module/trackback/">

<rdf:Description
rdf:about="[Entry Permalink]"
dc:title="[Entry Title]"
dc:identifier="[Entry Permalink]" />
trackback:ping="[http://yourserver.com/cgi-bin/receiver.cgi/][TrackBack ID]"
</rdf:RDF>
-->

위의 코드를 각 문서내에 적절히 삽입하시면 됩니다. []안의 내용은 삽입되는 문서에 맞게 수정되어야겠지요?
만약 트랙백을 보내기 전에 먼저 해당 문서를 읽어들여, 저 코드를 발견한다면 사용자가 receiver.cgi?id=xxx식으로 일일이 적어줄 필요가 없겠죠? MT등에서는 실제로, 엔트리 작성시 본문에서 언급된 모든 링크를 찾아 다니면서 저 아이템이 있으면 자동으로 트랙백을 걸어줍니다. 사용자가 트랙백할 주소를 일일이 지정하지 않아도 되니 편리하겠지요. 요런 기능도 여유가 되시면 구현하면 좋겠죠.

그외의 트랙백 관련 확장으로는, 카테고리 별 자동 트랙백(특정 카테고리로 입력되는 엔트리는 무조건 특정 주소로 자동으로 트랙백 보내기)과, 한번에 복수개의 트랙백 보내기등이 구현되면 될 것 같네요.

혹시 개발시에 문제가 되거나 궁금하신 점이 있으면 말씀하세요.

<출처 :
http://eouia.net >

'JAVA > JSP_Servlet' 카테고리의 다른 글

[링크]자바로 만든 블로그  (2) 2005.02.11
간단한 trackback ping 보내는 JSP ..  (0) 2005.02.04
[펌] 검색엔진 만들기  (0) 2005.01.21
[javamail] 제목 깨질때...  (0) 2005.01.18
[펌] 메일 헤더 구성 요소  (0) 2005.01.17
Posted by tornado
|