캠핑과 개발

maven jetty plugin 옵션 중에 scanIntervalSeconds 라는 옵션을 줘서 파일이 변경됬을 경우 일정시간 마다 서버를 리스타트 하는 기능이 있다.

이것과 비슷하게 지정된 특정 디렉토리에 파일이나 디렉토리가 생성, 삭제, 변경 되는 것을 모니터링 하는 클래스를 맨들어 보았다.

이것을 위해서 Jetty util 패키지에 있는 Scanner 클래스를 사용했다.

maven 사용자는 다음과 같은 dependency 를 추가한다.

  1. <dependency>
  2.     <groupId>org.eclipse.jetty</groupId>
  3.     <artifactId>jetty-util</artifactId>
  4.     <version>8.0.1.v20110908</version>
  5. </dependency>


maven 사용안하는 사람은 jetty-util-8.0.1.v20110908.jar 요파일을 클래스 패스에 추가 시켜 주면 된다. 

jetty-util-8.0.1.v20110908.jar


FileChangeScanner 클래스는 다음과 같다. DiscreteListener 클래스의 각각의 경우에 대해서 원하는 엑션을 취해주면 된다.

  1. package scanner;
  2.  
  3. import java.io.File;
  4. import java.io.FileFilter;
  5. import java.util.ArrayList;
  6. import java.util.List;
  7. import java.util.Timer;
  8. import java.util.TimerTask;
  9.  
  10. import org.eclipse.jetty.util.Scanner;
  11.  
  12. public class FileChangeScanner {
  13.     private Scanner scanner;
  14.  
  15.     public FileChangeScanner(String targetDir){
  16.         List<File> scanFiles = new ArrayList<File>();
  17.          
  18.         searchSubDirs(targetDir, scanFiles);
  19.          
  20.         scanner = new Scanner();
  21.         scanner.setScanInterval(1);          // 1초 간격으로 변경사항 스캔
  22.         scanner.setScanDirs(scanFiles);
  23.         scanner.setReportExistingFilesOnStartup(false);
  24.          
  25.         scanner.addListener(new Scanner.DiscreteListener() {
  26.             public void fileRemoved(String filename) throws Exception {
  27.                 System.out.println(filename + " is deleted");
  28.             }
  29.              
  30.             public void fileChanged(String filename) throws Exception {
  31.                 System.out.println(filename + " is changed");
  32.             }
  33.              
  34.             public void fileAdded(String filename) throws Exception {
  35.                 File f = new File(filename);
  36.                 if(f.isDirectory()) scanner.addScanDir(f);
  37.                  
  38.                 System.out.println(filename + " is added");
  39.             }
  40.         });
  41.     }
  42.      
  43.     public void start(){
  44.         try {
  45.             scanner.start();
  46.         } catch (Exception e) {
  47.             new RuntimeException(e);
  48.         }
  49.     }
  50.      
  51.     /**
  52.      * 하위 디렉토리들 찾기
  53.      * @param targetDir
  54.      * @param dirs
  55.      * @return
  56.      */
  57.     private List<File> searchSubDirs(String targetDir, final List<File> dirs){
  58.         File target = new File(targetDir);
  59.         target.listFiles(new FileFilter() {
  60.             public boolean accept(File file) {
  61.                 if( file.isDirectory() ) {
  62.                     dirs.add(file);
  63.                     searchSubDirs(file.toString(), dirs);
  64.                 }
  65.                 return false;
  66.             }
  67.         });
  68.          
  69.         return dirs;
  70.     }
  71.  
  72.     public static void main(String[] args) throws Exception{
  73.         // c:\test 하위 폴더 및 파일 변경 감시
  74.         new FileChangeScanner("c:\\test").start();
  75.          
  76.         // 실행하자 마자 프로그램이 종료되기 때문에 프로그램 종료방지를 위해 타이머 생성
  77.         // WAS 환경에서 돌릴때는 타이머 생성할 필요없음
  78.         Timer timer = new Timer();
  79.         timer.scheduleAtFixedRate(new TimerTask() {
  80.             public void run() {}
  81.         }60*100060*1000 );
  82.     }
  83. }


출처 : http://stove99.tistory.com/61

'DEVELOPMENT > Java' 카테고리의 다른 글

Ant-style path patterns  (0) 2019.02.07
spring @ControllerAdvice 설정 팁  (0) 2019.02.02
Java ImageIO 이미지 성능을위한 5 가지 팁  (0) 2017.01.18
<mvc:annotation-driven>이 하는 일  (0) 2016.07.28
java 파일 읽고 쓰기  (0) 2016.07.05


필요라이브러리 dom4j-1.6.1.jar


XML 쓰기

import org.jdom.Document;

import org.jdom.Element;

import org.jdom.input.SAXBuilder;

import org.jdom.output.XMLOutputter;


public class XMLWriterExample{

public static void main(String[] args) {

Element element = null;

Document document = null;

try{

SAXBuilder builder = new SAXBuilder();

element = new Element("root"); //rootElement 생성

 //하위 엘레먼트 생성후 속성 추가 및 text 추가

Element sub = new Element("sub").setAttribute("test1", "aaaa").setText("안녕하세요");

element.addContent(sub); //root Element에 sub Element 추가

document = new Document(element);

      

      //파일 생성

XMLOutputter out = new XMLOutputter();

out.output(document, new FileOutputStream(new File("d://test.xml")));

}catch(Exception e){

//TODO Exception 처리

}

}

}



XML 읽기

package url;


import java.io.FileOutputStream;

import java.net.URL;

import java.util.List;

import org.jdom.Document;

import org.jdom.Element;

import org.jdom.input.SAXBuilder;

import org.jdom.output.XMLOutputter;



public class Ex2 {

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

        URL url = new URL("http://openapi.naver.com/search?key=7d78ca29b1966e2cafee0327b181429d&target=movie&query=java&display=50&start=1&sort=sim");


        //xml이기 때문에 그냥 String을 이용하는 것이 아니라 JDOM으로 파싱하여 편하게 사용하고 싶습니다 (DOM구조)


        Document doc = new SAXBuilder().build(url);


        //root엘리먼트인 rss요소를 뽑아오기

        Element rss = doc.getRootElement();


        //rss의 자식인 channel요소를 얻고

        Element channel = rss.getChild("channel");

        //channel의 자식인 item요소들을 얻습니다.

        List<Element> itemList = channel.getChildren("item");


        //item 이라는게 책이다

        //책이름과 저자, 가격을 출력해 봅시다


        for(Element book : itemList){

            System.out.print("영화제목 : "+ book.getChildText("title") + " \t");

            //book.getChild("title").getText();

            System.out.print("감독 : " + book.getChildText("director") + "\t");

            System.out.println("배우 : " + book.getChildText("actor"));

        }

        //실제파일로 저장해 봅시다.

        XMLOutputter xout = new XMLOutputter();


        //파일로 저장

        xout.output(doc, new FileOutputStream("c:/movie.xml"));

        //콘솔창 출력

        xout.output(doc, System.out);

    }


'DEVELOPMENT > Java' 카테고리의 다른 글

java 화면캡쳐 샘플  (0) 2013.12.04
Java Process Kill Script  (0) 2013.09.04
eclipse SWT/JFace 라이브러리 추가  (0) 2013.06.12
java openGL  (0) 2012.08.23
자바 정규표현식  (0) 2012.05.17

자바의 정규표현식은 J2SE 1.4 부터 지원되지 시작했습니다. 관련된 주요 클래스들는 java.util.regex 팩키지에 있습니다.

Pattern 클래스

Pattern 객체는 Perl 문법과 비슷한 형태로 정의된 정규표현식을 나타냅니다. 문자열로 정의한 정규표현식은 사용되기 전에 반드시 Pattern 클래스의 인스턴스로 컴파일되어야 합니다. 컴파일된 패턴은 Matcher 객체를 만드는 데 사용되며, Matcher 객체는 임의의 입력 문자열이 패턴에 부합되는 지 여부를 판가름하는 기능을 담당하합니다. 또한 Pattern 객체들은 비상태유지 객체들이기 때문에 여러 개의 Matcher 객체들이 공유할 수 있습니다.

Matcher 클래스

Matcher 객체는 특정한 문자열이 주어진 패턴과 일치하는가를 알아보는데 이용됩니다. Matcher 클래스의 입력값으로는 CharSequence라는 새로운 인터페이스가 사용되는데 이를 통해 다양한 형태의 입력 데이터로부터 문자 단위의 매칭 기능을 지원 받을 수 있습니다. 기본적으로 제공되는 CharSequence 객체들은 CharBuffer, String, StringBuffer 클래스가 있습니다.

Matcher 객체는 Pattern 객체의 matcher 메소드를 통해 얻어진다. Matcher 객체가 일단 만들어지면 주로 세 가지 목적으로 사용됩다.

  • 주어진 문자열 전체가 특정 패턴과 일치하는 가를 판단(matches).
  • 주어진 문자열이 특정 패턴으로 시작하는가를 판단(lookingAt).
  • 주어진 문자열에서 특정 패턴을 찾아낸다(find).

이들 메소드는 성공 시 true를 실패 시 false 를 반환합니다.

또한 특정 문자열을 찾아 새로운 문자열로 교체하는 기능도 제공됩니다. appendRepalcement 메소드는 일치하는 패턴이 나타날 때까지의 모든 문자들을 버퍼로 옮기고 찾아진 문자열 대신 교체 문자열로 채워 넣습니다. 또한 appendTail 메소드는 캐릭터 시퀀스의 현재 위치 이후의 문자들을 버퍼에 복사해 넣습니다. 다음 절에 나오는 예제 코드를 참고하도록 합시다.

CharSequence 인터페이스

CharSequence 인터페이스는 다양한 형태의 캐릭터 시퀀스에 대해 일관적인 접근 방법을 제공하기 위해 새로 생겨났으며, java.lang 패키지에 존재합니다. 기본적으로 String, StringBuffer, CharBuffer 클래스가 이를 구현하고 있으므로 적절한 것을 골라 사용하면 되며, 인터페이스가 간단하므로 필요하면 직접 이를 구현해 새로 하나 만들어도 됩니다.



자바 정규표현식 사용 예제

기본 사용 예제

소스

import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * 정규표현식 기본 사용 예제 
 * 
 * @author   Sehwan Noh <sehnoh at java2go.net>
 * @version  1.0 - 2006. 08. 22
 * @since    JDK 1.4
 */
public class RegExTest01 {

    public static void main(String[] args) {

        Pattern p = Pattern.compile("a*b");
        Matcher m = p.matcher("aaaaab");
        boolean b = m.matches();
        
        if (b) {
            System.out.println("match");
        } else {
            System.out.println("not match");
        }
    }
}

결과

match

문자열 치환하기

소스

import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * 문자열 치환 예제
 * 
 * @author   Sehwan Noh <sehnoh at java2go.net>
 * @version  1.0 - 2006. 08. 22
 * @since    JDK 1.4
 */
public class RegExTest02 {

    public static void main(String[] args) {

        Pattern p = Pattern.compile("cat");
        Matcher m = p.matcher("one cat two cats in the yard");
        
        StringBuffer sb = new StringBuffer();
        while (m.find()) {
            m.appendReplacement(sb, "dog");
        }
        m.appendTail(sb);
        System.out.println(sb.toString());
        
        // or
        //String str = m.replaceAll("dog");
        //System.out.println(str);
    }
}

결과

one dog two dogs in the yard

이메일주소 유효검사

소스

import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * 이메일주소 유효검사
 * 
 * @author   Sehwan Noh <sehnoh at java2go.net>
 * @version  1.0 - 2006. 08. 22
 * @since    JDK 1.4
 */
public class RegExTest03 {
    
    public static boolean isValidEmail(String email) {
        Pattern p = Pattern.compile("^(?:\\w+\\.?)*\\w+@(?:\\w+\\.)+\\w+$");
        Matcher m = p.matcher(email);
        return m.matches();
    }

    public static void main(String[] args) {
        
        String[] emails = { "test@abc.com", "a@.com", "abc@mydomain" };
        
        for (int i = 0; i < emails.length; i ++) {
            if (isValidEmail(emails[i])) {
                System.out.println(emails[i]);
            }
        }
    }
}

결과

test@abc.com

HTML 태그 제거

소스

import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * HTML 태그 제거
 * 
 * @author   Sehwan Noh <sehnoh at java2go.net>
 * @version  1.0 - 2006. 08. 22
 * @since    JDK 1.4
 */
public class RegExTest04 {

    public static String stripHTML(String htmlStr) {
        Pattern p = Pattern.compile("<(?:.|\\s)*?>");
        Matcher m = p.matcher(htmlStr);
        return m.replaceAll("");
    }
    
    public static void main(String[] args) {
        String htmlStr = "<html><body><h1>Java2go.net</h1>"
            + " <p>Sehwan@Noh's Personal Workspace...</p></body></html>";
        System.out.println(stripHTML(htmlStr));        
    }
}

결과

Java2go.net Sehwan@Noh's Personal Workspace...

HTML 링크 만들기

소스

import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * HTML 링크 만들기
 * 
 * @author   Sehwan Noh <sehnoh at java2go.net>
 * @version  1.0 - 2006. 08. 22
 * @since    JDK 1.4
 */
public class RegExTest05 {

    public static String linkedText(String sText) {
        Pattern p = Pattern.compile("(http|https|ftp)://[^\\s^\\.]+(\\.[^\\s^\\.]+)*");
        Matcher m = p.matcher(sText);

        StringBuffer sb = new StringBuffer();
        while (m.find()) {
            m.appendReplacement(sb, "<a href='" + m.group()+"'>" + m.group() + "</a>");
        }
        m.appendTail(sb);

        return sb.toString();
    }    
        
    public static void main(String[] args) {        
        String strText = "My homepage URL is http://www.java2go.net/home/index.html.";
        System.out.println(linkedText(strText));
    }
}

결과

My homepage URL is <a href='http://www.java2go.net/index.html'>http://www.java2go.net/index.html</a>.

금지어 필터링하기

소스

import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * 금지어 필터링하기
 * 
 * @author   Sehwan Noh <sehnoh at java2go.net>
 * @version  1.0 - 2006. 08. 22
 * @since    JDK 1.4
 */
public class RegExTest06 {
    
    public static String filterText(String sText) {
        Pattern p = Pattern.compile("fuck|shit|개새끼", Pattern.CASE_INSENSITIVE);
        Matcher m = p.matcher(sText);

        StringBuffer sb = new StringBuffer();
        while (m.find()) {
            //System.out.println(m.group());
            m.appendReplacement(sb, maskWord(m.group()));
        }
        m.appendTail(sb);
        
        //System.out.println(sb.toString());
        return sb.toString();
    }
    
    public static String maskWord(String word) {
        StringBuffer buff = new StringBuffer();
        char[] ch = word.toCharArray();
        for (int i = 0; i < ch.length; i++) {
            if (i < 1) {
                buff.append(ch[i]);
            } else {
                buff.append("*");
            }
        }
        return buff.toString();
    }
    
    public static void main(String[] args) {
        String sText = "Shit! Read the fucking manual. 개새끼야.";        
        System.out.println(filterText(sText));
    }
}

결과

S***! Read the f***ing manual. 개**야.


Java Regex References

A regular expression, specified as a string, must first be compiled into an instance of this class. The resulting pattern can then be used to create a Matcher object that can match arbitrary character sequences against the regular expression. All of the state involved in performing a match resides in the matcher, so many matchers can share the same pattern.

A typical invocation sequence is thus

 Pattern p = Pattern.compile("a*b");
 Matcher m = p.matcher("aaaaab");
 boolean b = m.matches();

A matches method is defined by this class as a convenience for when a regular expression is used just once. This method compiles an expression and matches an input sequence against it in a single invocation. The statement

 boolean b = Pattern.matches("a*b", "aaaaab");
is equivalent to the three statements above, though for repeated matches it is less efficient since it does not allow the compiled pattern to be reused.

Instances of this class are immutable and are safe for use by multiple concurrent threads. Instances of the Matcher class are not safe for such use.

Summary of regular-expression constructs

ConstructMatches
 
Characters
xThe character x
\\The backslash character
\0nThe character with octal value 0n (0 <= n <= 7)
\0nnThe character with octal value 0nn (0 <= n <= 7)
\0mnnThe character with octal value 0mnn (0 <= m <= 3, 0 <= n <= 7)
\xhhThe character with hexadecimal value 0xhh
\uhhhhThe character with hexadecimal value 0xhhhh
\tThe tab character ('\u0009')
\nThe newline (line feed) character ('\u000A')
\rThe carriage-return character ('\u000D')
\fThe form-feed character ('\u000C')
\aThe alert (bell) character ('\u0007')
\eThe escape character ('\u001B')
\cxThe control character corresponding to x
 
Character classes
[abc]a, b, or c (simple class)
[^abc]Any character except a, b, or c (negation)
[a-zA-Z]a through z or A through Z, inclusive (range)
[a-d[m-p]]a through d, or m through p: [a-dm-p] (union)
[a-z&&[def]]d, e, or f (intersection)
[a-z&&[^bc]]a through z, except for b and c: [ad-z] (subtraction)
[a-z&&[^m-p]]a through z, and not m through p: [a-lq-z](subtraction)
 
Predefined character classes
.Any character (may or may not match line terminators)
\dA digit: [0-9]
\DA non-digit: [^0-9]
\sA whitespace character: [ \t\n\x0B\f\r]
\SA non-whitespace character: [^\s]
\wA word character: [a-zA-Z_0-9]
\WA non-word character: [^\w]
 
POSIX character classes (US-ASCII only)
\p{Lower}A lower-case alphabetic character: [a-z]
\p{Upper}An upper-case alphabetic character:[A-Z]
\p{ASCII}All ASCII:[\x00-\x7F]
\p{Alpha}An alphabetic character:[\p{Lower}\p{Upper}]
\p{Digit}A decimal digit: [0-9]
\p{Alnum}An alphanumeric character:[\p{Alpha}\p{Digit}]
\p{Punct}Punctuation: One of !"#$%&'()*+,-./:;<=>?@[\]^_`{|}~
\p{Graph}A visible character: [\p{Alnum}\p{Punct}]
\p{Print}A printable character: [\p{Graph}\x20]
\p{Blank}A space or a tab: [ \t]
\p{Cntrl}A control character: [\x00-\x1F\x7F]
\p{XDigit}A hexadecimal digit: [0-9a-fA-F]
\p{Space}A whitespace character: [ \t\n\x0B\f\r]
 
java.lang.Character classes (simple java character type)
\p{javaLowerCase}Equivalent to java.lang.Character.isLowerCase()
\p{javaUpperCase}Equivalent to java.lang.Character.isUpperCase()
\p{javaWhitespace}Equivalent to java.lang.Character.isWhitespace()
\p{javaMirrored}Equivalent to java.lang.Character.isMirrored()
 
Classes for Unicode blocks and categories
\p{InGreek}A character in the Greek block (simple block)
\p{Lu}An uppercase letter (simple category)
\p{Sc}A currency symbol
\P{InGreek}Any character except one in the Greek block (negation)
[\p{L}&&[^\p{Lu}]] Any letter except an uppercase letter (subtraction)
 
Boundary matchers
^The beginning of a line
$The end of a line
\bA word boundary
\BA non-word boundary
\AThe beginning of the input
\GThe end of the previous match
\ZThe end of the input but for the final terminator, if any
\zThe end of the input
 
Greedy quantifiers
X?X, once or not at all
X*X, zero or more times
X+X, one or more times
X{n}X, exactly n times
X{n,}X, at least n times
X{n,m}X, at least n but not more than m times
 
Reluctant quantifiers
X??X, once or not at all
X*?X, zero or more times
X+?X, one or more times
X{n}?X, exactly n times
X{n,}?X, at least n times
X{n,m}?X, at least n but not more than m times
 
Possessive quantifiers
X?+X, once or not at all
X*+X, zero or more times
X++X, one or more times
X{n}+X, exactly n times
X{n,}+X, at least n times
X{n,m}+X, at least n but not more than m times
 
Logical operators
XYX followed by Y
X|YEither X or Y
(X)X, as a capturing group
 
Back references
\nWhatever the nth capturing group matched
 
Quotation
\Nothing, but quotes the following character
\QNothing, but quotes all characters until \E
\ENothing, but ends quoting started by \Q
 
Special constructs (non-capturing)
(?:X)X, as a non-capturing group
(?idmsux-idmsux) Nothing, but turns match flags on - off
(?idmsux-idmsux:X)  X, as a non-capturing group with the given flags on - off
(?=X)X, via zero-width positive lookahead
(?!X)X, via zero-width negative lookahead
(?<=X)X, via zero-width positive lookbehind
(?<!X)X, via zero-width negative lookbehind
(?>X)X, as an independent, non-capturing group

Backslashes, escapes, and quoting

The backslash character ('\') serves to introduce escaped constructs, as defined in the table above, as well as to quote characters that otherwise would be interpreted as unescaped constructs. Thus the expression \\ matches a single backslash and \{ matches a left brace.

It is an error to use a backslash prior to any alphabetic character that does not denote an escaped construct; these are reserved for future extensions to the regular-expression language. A backslash may be used prior to a non-alphabetic character regardless of whether that character is part of an unescaped construct.

Backslashes within string literals in Java source code are interpreted as required by the Java Language Specification as either Unicode escapes or other character escapes. It is therefore necessary to double backslashes in string literals that represent regular expressions to protect them from interpretation by the Java bytecode compiler. The string literal "\b", for example, matches a single backspace character when interpreted as a regular expression, while "\\b" matches a word boundary. The string literal "\(hello\)" is illegal and leads to a compile-time error; in order to match the string (hello) the string literal "\\(hello\\)" must be used.

Character Classes

Character classes may appear within other character classes, and may be composed by the union operator (implicit) and the intersection operator (&&). The union operator denotes a class that contains every character that is in at least one of its operand classes. The intersection operator denotes a class that contains every character that is in both of its operand classes.

The precedence of character-class operators is as follows, from highest to lowest:

1    Literal escape    \x
2    Grouping[...]
3    Rangea-z
4    Union[a-e][i-u]
5    Intersection[a-z&&[aeiou]]

Note that a different set of metacharacters are in effect inside a character class than outside a character class. For instance, the regular expression . loses its special meaning inside a character class, while the expression - becomes a range forming metacharacter.

Line terminators

line terminator is a one- or two-character sequence that marks the end of a line of the input character sequence. The following are recognized as line terminators:

  • A newline (line feed) character ('\n'),
  • A carriage-return character followed immediately by a newline character ("\r\n"),
  • A standalone carriage-return character ('\r'),
  • A next-line character ('\u0085'),
  • A line-separator character ('\u2028'), or
  • A paragraph-separator character ('\u2029).

If UNIX_LINES mode is activated, then the only line terminators recognized are newline characters.

The regular expression . matches any character except a line terminator unless the DOTALL flag is specified.

By default, the regular expressions ^ and $ ignore line terminators and only match at the beginning and the end, respectively, of the entire input sequence. If MULTILINE mode is activated then ^ matches at the beginning of input and after any line terminator except at the end of input. When in MULTILINE mode $ matches just before a line terminator or the end of the input sequence.

Groups and capturing

Capturing groups are numbered by counting their opening parentheses from left to right. In the expression ((A)(B(C))), for example, there are four such groups:

1    ((A)(B(C)))
2    (A)
3    (B(C))
4    (C)

Group zero always stands for the entire expression.

Capturing groups are so named because, during a match, each subsequence of the input sequence that matches such a group is saved. The captured subsequence may be used later in the expression, via a back reference, and may also be retrieved from the matcher once the match operation is complete.

The captured input associated with a group is always the subsequence that the group most recently matched. If a group is evaluated a second time because of quantification then its previously-captured value, if any, will be retained if the second evaluation fails. Matching the string "aba" against the expression (a(b)?)+, for example, leaves group two set to "b". All captured input is discarded at the beginning of each match.

Groups beginning with (? are pure, non-capturing groups that do not capture text and do not count towards the group total.

Unicode support

This class is in conformance with Level 1 of Unicode Technical Standard #18: Unicode Regular Expression Guidelines, plus RL2.1 Canonical Equivalents.

Unicode escape sequences such as \u2014 in Java source code are processed as described in ?.3 of the Java Language Specification. Such escape sequences are also implemented directly by the regular-expression parser so that Unicode escapes can be used in expressions that are read from files or from the keyboard. Thus the strings "\u2014" and "\\u2014", while not equal, compile into the same pattern, which matches the character with hexadecimal value 0x2014.

Unicode blocks and categories are written with the \p and \P constructs as in Perl. \p{prop} matches if the input has the property prop, while \P{prop} does not match if the input has that property. Blocks are specified with the prefix In, as in InMongolian. Categories may be specified with the optional prefix Is: Both \p{L} and \p{IsL} denote the category of Unicode letters. Blocks and categories can be used both inside and outside of a character class.

The supported categories are those of The Unicode Standard in the version specified by the Character class. The category names are those defined in the Standard, both normative and informative. The block names supported by Pattern are the valid block names accepted and defined by UnicodeBlock.forName.

Categories that behave like the java.lang.Character boolean ismethodname methods (except for the deprecated ones) are available through the same \p{prop} syntax where the specified property has the name javamethodname.

Comparison to Perl 5

The Pattern engine performs traditional NFA-based matching with ordered alternation as occurs in Perl 5.

Perl constructs not supported by this class:

  • The conditional constructs (?{X}) and (?(condition)X|Y),

  • The embedded code constructs (?{code}) and (??{code}),

  • The embedded comment syntax (?#comment), and

  • The preprocessing operations \l \u, \L, and \U.

Constructs supported by this class but not by Perl:

  • Possessive quantifiers, which greedily match as much as they can and do not back off, even when doing so would allow the overall match to succeed.

  • Character-class union and intersection as described above.

Notable differences from Perl:

  • In Perl, \1 through \9 are always interpreted as back references; a backslash-escaped number greater than 9 is treated as a back reference if at least that many subexpressions exist, otherwise it is interpreted, if possible, as an octal escape. In this class octal escapes must always begin with a zero. In this class, \1 through \9 are always interpreted as back references, and a larger number is accepted as a back reference if at least that many subexpressions exist at that point in the regular expression, otherwise the parser will drop digits until the number is smaller or equal to the existing number of groups or it is one digit.

  • Perl uses the g flag to request a match that resumes where the last match left off. This functionality is provided implicitly by the Matcher class: Repeated invocations of the find method will resume where the last match left off, unless the matcher is reset.

  • In Perl, embedded flags at the top level of an expression affect the whole expression. In this class, embedded flags always take effect at the point at which they appear, whether they are at the top level or within a group; in the latter case, flags are restored at the end of the group just as in Perl.

  • Perl is forgiving about malformed matching constructs, as in the expression *a, as well as dangling brackets, as in the expression abc], and treats them as literals. This class also accepts dangling brackets but is strict about dangling metacharacters like +, ? and *, and will throw a PatternSyntaxException if it encounters them.



    2010/09/30 - [개발 이야기/JAVA] - JAVA를 이용한 정규식 간단 사용법


    [출처]http://www.java2go.net/java/java_regex.html


'DEVELOPMENT > Java' 카테고리의 다른 글

eclipse SWT/JFace 라이브러리 추가  (0) 2013.06.12
java openGL  (0) 2012.08.23
대용량 데이터 처리를 위한 JAVA Tunning(튜닝)  (0) 2012.05.17
Apache Daemon 사용하기  (0) 2012.05.17
Quartz 문서  (0) 2011.07.13

대용량 데이타를 처리할 때는 1초 아니 0.001초도 아껴야 한다.
아래의 팁을 활용하여 수행 속도를 줄여 보자.

- 64bit OS 를 사용하고, 아끼지 말고 RAM을 추가해라.
CPU만 지원한다면, 무조건 64bit 운영체제를 설치해서 사용하라. RAM이 4GB 이하라도 설치해라.
CPU의 처리 단위 증가 뿐만 아니라, 메모리를 최대한 사용할 수 있기 때문에 GC의 회수가 감소하여 속도가 더 높아진다.

내 경우, Windows XP 32bit 에서 최대메모리가 약 1.5G 이상 불가능 했는데, 
Windows7 64bit 에서는 OS가 사용하는 메모리를 제외하고 모두 JVM이 사용할 수 있었다. 
1GB 파일을 생성하는데, 대략 5~10배 정도의 실행 시간 차이가 있었다.

- xms, xmx 옵션을 최대한 크게, 그리고 같게 잡는다.
다르게 잡아 줄 경우, 실행 도중에 메모리 할당이 이루어져서 실행 시간이 증가한다.

(1) Windows7 64bit (물리적 메모리가 4g인 경우)
java -jar -xms4g -xmx4g xxx.jar  
재미있는 것은 Windows7 64bit 에서는 이 두 값을 물리적 메모리 보다 크게 잡아도 실행이 된다. 이유는 모르겠다.

(2) Windows XP 32bit (물리적 메모리가 4g인 경우)
java -jar -xms1.5g -xmx1.5g xxx.jar  
아마  1~1.5 사이까지 가능할 것이다. 넘어 가면 실행 자체가 안 되는데 실행가능한 최대값을 찾아 사용하자.

- String 대신 StringBuilder를 사용하라. (단, 스레드를 사용할 경우에는 StringBuffer를 사용하라.)
문자열에 여러 가지 조작을 가할 경우이다. 변하지 않는 문자열의 경우, String을 사용하면 된다.
String을 사용하여도 컴파일러가 적당히 변환해 준다고는 하지만, 컴파일러는 항상 개발자보다 무식하다.

참고로 실행 속도는 StringBuilder > StringBuffer >> String 이며,  
메모리사용량은 StringBuilder = StringBuffer << String 이다.
자세한 비교는 http://hongsgo.egloos.com/2033998 를 참고하라.

- String.split() 대신 StringTokenizer.nextToken()을 사용하라.
StringTokenizer.nextToken()는 사용이 불편하지만 빠르며, String.split()는 사용하기 편하지만 느리다.
token이 빈 문자열일 경우, String.split() 은 ""를 해당 배열 원소에 저장해 주지만, StringTokenizer.nextToken() 는 null을 리턴한다.
단 정규식을 구분자로 사용할 경우는 어쩔 수 없이  String.split() 을 사용해야 한다.
성능비교는  http://ir.bagesoft.com/622 를 참고하라.
 
- Buffered~계열 클래스 (BufferedReader, BufferedWriter 등) 를 사용하라.
이건 너무 쉬운 내용이라. 자세한 것은 검색을 통해 찾아 봐라.

'DEVELOPMENT > Java' 카테고리의 다른 글

java openGL  (0) 2012.08.23
자바 정규표현식  (0) 2012.05.17
Apache Daemon 사용하기  (0) 2012.05.17
Quartz 문서  (0) 2011.07.13
[JAVA] 동적 캐스팅  (0) 2011.06.10

1. 데몬(daemon) 이란?
 주기적인 서비스 요청을 처리하기 위해서 커널상에 백그라운드 모드로 실행되는 프로세스로, 메모리 관리 방법에 따라 단독 데몬과 xinetd로 분리된다.

단독데몬
 항상 백그라운드 모드로 실행되고 메모리를 상대적으로 많이 소비한다. 그러나 서비스(응답속도)가 빠르다. httpd와 같은 웹서비스 데몬이 대표적.
xinetd(슈퍼데몬)
 요청이 있을때마다 xinetd가 서비스를 싱행시켜주므로 메모리 소비가 적다. 그러나 단독데몬에 비해 상대적으로 서비스 속도가 느리다.


 2. 간단한 자바 데몬 만들기
 nohup을 이용해서 java를 실행시킨다.

 터미널이 종료될 때(쉘이 종료될 때) 프로세스가 죽는 이유는 해당 프로세스가 쉘의 자식 프로세스 이기 때문이다. 따라서, 부모 프로세스가 죽을대 던지는 SIGHUP을 자식 프로세스가 받게 된다.

 nohup은 부모 프로세스가 죽을때 자식 프로세스에게 SIGHUP을 던지지 않는 프로세스를 말한다.
$ nohup java 클래스명 &

  
 사용하기 편한 장점은 있으나, 문제는 중지를 시킬수 없다. 즉, 해당 프로세스 ID를 찾아내 kill하는 수 밖에 없다. 물론 파일 체크 기법, 소켓을 이용한 제어 방법등을 사용할 수 있지만 스스로 구현해야 하는 번거로움이 있다.


 3. apache commons daemon 이용하기
 Java는 UNIX의 시그널 처리를 할수 없기때문에, 중지 신호(TERM signal)를 처리하기 위해서는 따로 구현을 해야한다. 이런 번거로움을 해결하기 위해 자카르타의 하위 프로젝트중의 commons daemon을 이용한다. commons daemon은 중지 신호를 받으면 미리 지정된 메소드를 실행한다.

 ** 다운로드: http://commons.apache.org/daemon/

 UNIX용 Jsvc와 윈도우용 Procrun 있다.
 여기서는 Jsvc를 이용해보도록 하겠다.

 

 commons daemon을 다운로드해 압축을 해제하면 위 그림과 같다.
 commons-daemon.jar 파일은 Java프로젝트 lib폴더에 복사해둔다.

 


 bin폴더의 압축파일을 해제하면 jsvc-src라는 폴더가 나온다.
 폴더의 내용은 위와 같다.
 commons daemon을 사용하기 위해서는 바로 여기서 jsvc를 빌드해줘야 한다.
 빌드환경은 다음과 같다.(리눅스에서 빌드해야한다.)

GNU AutoConf(at least 2.53)
ANSI-C compliant compiler(GCC is good)
GNU Make
A Java Platform 2 compliant SDK

 여기서부터는 ubuntu 8.10 환경에서 진행하도록 한다.

 먼저 JDK가 설치되어 있지 않다면 JDK를 설치한다.
 $ sudo apt-get install sun-java6-jdk


 JDK가 설치되는 경로는 /usr/lib/jvm/java-6-sun 이다.

 gcc 및 make 가 설치되있지 않다면 아래 명령를 이용해 한방에 설치한다.
 $ sudo apt-get install build-essential


 AutoConf가 설치되있지 않다면 AutoConf를 설치한다.
 $ sudo apt-get install autoconf


 Jsvc를 빌드하는 방법은 다음과 같다.

support/buildconf.sh
./configure --with-java=/usr/lib/jvm/java-6-sun
make


참고 방법

mkdir /root/commons-daemon

cd /root/commons-daemon

wget http://www.apache.org/dist/commons/daemon/binaries/1.0.5/commons-daemon-1.0.5.jar
wget http://mirror.apache-kr.org//commons/daemon/source/commons-daemon-1.0.5-src.tar.gz
tar zxvf commons-daemon-1.0.5-src.tar.gz
cd commons-daemon-1.0.5-src/src/native/unix/
support/buildconf.sh
./configure --with-java=/opt/jdk1.6.0_24/
make
mv jsvc /root/commons-daemon

 

 빌드가 성공적으로 이루어졌다면 위 그림과 같이 jsvc가 만들어진것을 확인할 수 있다. 이후 이 파일을 가지고 Java 데몬을 실행한다.

 Java 데몬을 만들려면 org.apache.commons.daemon.Daemon 인터페이스의 init, start, stop, destory 메소드를 구현해야 한다.


샘플

package com.bagesoft.test.daemon;
 
import org.apache.commons.daemon.Daemon;
import org.apache.commons.daemon.DaemonContext;
import org.apache.commons.daemon.DaemonInitException;
 
public class TestDaemon implements Daemon, Runnable {
    private String status = "";
    private int no = 0;
    private Thread thread = null;
 
    @Override
    public void init(DaemonContext context) throws DaemonInitException,
            Exception {
        System.out.println("init...");
        String[] args = context.getArguments();
        if (args != null) {
            for (String arg : args) {
                System.out.println(arg);
            }
        }
        status = "INITED";
        this.thread = new Thread(this);
        System.out.println("init OK.");
        System.out.println();
    }
 
    @Override
    public void start() {
        System.out.println("status: " + status);
        System.out.println("start...");
        status = "STARTED";
        this.thread.start();
        System.out.println("start OK.");
        System.out.println();
    }
 
    @Override
    public void stop() throws Exception {
        System.out.println("status: " + status);
        System.out.println("stop...");
        status = "STOPED";
        this.thread.join(10);
        System.out.println("stop OK.");
        System.out.println();
    }
 
    @Override
    public void destroy() {
        System.out.println("status: " + status);
        System.out.println("destroy...");
        status = "DESTROIED";
        System.out.println("destroy OK.");
        System.out.println();
    }
 
    @Override
    public void run() {
        while (true) {
            System.out.println(no);
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            if (no > 1000) {
                break;
            }
            no++;
        }
    }
}


init에서 초기화, start에서 처리할 작업을 별도의 쓰레드로 생성해서 실행한다. start 메소드 호출후 반드시 return 되어야 데몬이 시그널 처리를 제대로 할 수 있다.
 stop, destroy 는 중지 신호가 오면 차례되로 호출된다.


 이제 실행/중지 스크립트를 작성한다.
 Jsvc를 싱행하는데 필요한것은 실행하는 계정(user), JAVA_HOME(-home), 프로세스ID 파일(-pidfile), 출력지정(-outfile, -errfile), 클래스파일(-cp) 등이 있다.


JAVA_HOME=/opt/jdk1.6.0_24
JSVC=/root/commons-daemon/jsvc
USER=root
 
DAEMON_HOME=/root/commons-daemon
PID_FILE=$DAEMON_HOME/daemon.pid
OUT_FILE=$DAEMON_HOME/daemon.out
#ERR_FILE=$DAEMON_HOME/daemon.err
 
CLASSPATH=\
$DAEMON_HOME/commons-daemon-1.0.5.jar:\
$DAEMON_HOME/BageSoft.jar
 
MAIN_CLASS=com.bagesoft.test.daemon.TestDaemon
case "$1" in
  start)
    #
    # Start Daemon
    #
    rm -f $OUT_FILE
    $JSVC \
    -user $USER \
    -java-home $JAVA_HOME \
    -pidfile $PID_FILE \
    -outfile $OUT_FILE \
    -errfile $OUT_FILE \
    -cp $CLASSPATH \
    $MAIN_CLASS
    #
    # To get a verbose JVM
    #-verbose \
    # To get a debug of jsvc.
    #-debug \
    exit $?
    ;;
 
  stop)
    #
    # Stop Daemon
    #
    $JSVC \
    -stop \
    -nodetach \
    -java-home $JAVA_HOME \
    -pidfile $PID_FILE \
    -outfile $OUT_FILE \
    -errfile $OUT_FILE \
    -cp $CLASSPATH \
    $MAIN_CLASS
    exit $?
    ;;
 
  *)
    echo "[Usage] TestDaemon.sh start | stop"
    exit 1;;

esac 

 

이제 Java 데몬을 실행하기 위한 모든 준비를 마쳤다.

위에서 작성한 스크립트 파일을 이용해 Java 데몬을 실핸한다.
 정상적으로 데몬이 시작된다면 PID_FILE에 지정한 파일이 생성된것을 확인할 수 있다.

 

 

데몬을 중지하려면 start 대신 stop을 입력으로 스크립트를 실행하면 된다.


[출처] http://ultteky.egloos.com/10877963

'DEVELOPMENT > Java' 카테고리의 다른 글

자바 정규표현식  (0) 2012.05.17
대용량 데이터 처리를 위한 JAVA Tunning(튜닝)  (0) 2012.05.17
Quartz 문서  (0) 2011.07.13
[JAVA] 동적 캐스팅  (0) 2011.06.10
log4jdbc를 활용하여 쿼리 로그 남기기  (0) 2011.06.01


 Object something = new Integer(123);
 String theType = "java.lang.Number";
 Class<? extends Number> theClass = Class.forName(theType).asSubclass(Number.class);
 Number obj = theClass.cast(something);

'DEVELOPMENT > Java' 카테고리의 다른 글

Apache Daemon 사용하기  (0) 2012.05.17
Quartz 문서  (0) 2011.07.13
log4jdbc를 활용하여 쿼리 로그 남기기  (0) 2011.06.01
Commons-lang, Commons-io 사용 샘플  (0) 2011.06.01
JAVA Tip  (0) 2011.04.13


1. 필요한 라이브러리 다운 로드
 - log4jdbc3-1.1.jar(If you are using JDK 1.4 or 1.5, you should use the JDBC 3 version of log4jdbc.)
 - slf4j-api-1.5.0.jar(log4jdbc와 logging 서비스 연동을 위한 API 제공)
 - slf4j-log4j12-1.5.2.jar(log4jdbc와 Log4j 기반의 Logging 서비스 연동을 위한 구현 라이브러리 제공)

2. DataSource 서비스 속성 정의 파일 정의

<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource"
destroy-method="close">
<property name="driverClassName"
value="net.sf.log4jdbc.DriverSpy"/>
<property name="url"
value="jdbc:log4jdbc:mysql://localhost:3306/member?useUnicode=true
&characterEncoding=utf8"/>
<property name="username" value="member"/>
<property name="password" value="member"/>
<property name="maxActive" value="30"/>
<property name="maxIdle" value="3"/>
<property name="maxWait" value="-1"/>
</bean>

3. Logger 정의

 - jdbc.sqlonly : SQL문만을 로그로 남기며, PreparedStatement일 경우 관련된 argument 값으로 대체된 SQL문이 보여진다.
 - jdbc.sqltiming : SQL문과 해당 SQL을 실행시키는데 수행된 시간 정보(milliseconds)를 포함한다.
 - jdbc.audit : ResultSet을 제외한 모든 JDBC 호출 정보를 로그로 남긴다. 많은 양의 로그가 생성되므로 특별히 JDBC 문제를 추적해야 할 필요가 있는 경우를 제외하고는 사용을 권장하지 않는다.
 - jdbc.resultset : ResultSet을 포함한 모든 JDBC 호출 정보를 로그로 남기므로 매우 방대한 양의 로그가 생성된다.
 - 예) log4j.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE log4j:configuration SYSTEM "log4j.dtd">
<log4j:configuration
xmlns:log4j="http://jakarta.apache.org/log4j/" debug="false">

<appender name="console" class="org.apache.log4j.ConsoleAppender">
<layout class="org.apache.log4j.PatternLayout">
<param name="ConversionPattern" value="%d %5p [%c] %m%n" />
</layout>
</appender>

<appender name="rollingFile"
class="org.apache.log4j.RollingFileAppender">
<param name="File" value="C:\\logs\\error\\error.log"/>
<param name="Append" value="true"/>
<param name="MaxFileSize" value="100MB"/>
<param name="MaxBackupIndex" value="2"/>
<layout class="org.apache.log4j.PatternLayout">
<param name="ConversionPattern" value="%d %p [%c]-%m%n" />
</layout>
</appender>

<logger name="org.springframework.jdbc">
<level value="DEBUG"/>
<appender-ref ref="console"/>
</logger>

<logger name="com.mimul">
<level value="DEBUG"/>
<appender-ref ref="console"/>
<appender-ref ref="rollingFile"/>
</logger>

<logger name="org.apache.struts">
<level value="DEBUG"/>
<appender-ref ref="console"/>
<appender-ref ref="rollingFile"/>
</logger>

<!-- log SQL (pre-execution) plus exceptions caused by SQL -->
<logger name="jdbc.sqlonly" additivity="false">
<level value="DEBUG" />
<appender-ref ref="console" />
<appender-ref ref="rollingFile"/>
</logger>

<!-- log SQL with timing information, post execution -->
<logger name="jdbc.sqltiming" additivity="false">
<level value="DEBUG" />
<appender-ref ref="console" />
<appender-ref ref="rollingFile"/>
</logger>

<!-- only use the two logs below to trace ALL JDBC information,
NOTE: This can be very voluminous! -->
<!-- log all jdbc calls except ResultSet calls -->
<logger name="jdbc.audit" additivity="false">
<level value="DEBUG" />
<appender-ref ref="console" />
<appender-ref ref="rollingFile"/>
</logger>

<!-- log the jdbc ResultSet calls -->
<logger name="jdbc.resultset" additivity="false">
<level value="DEBUG" />
<appender-ref ref="console" />
<appender-ref ref="rollingFile"/>
</logger>

<!-- this log is for internal debugging of log4jdbc, itself -->
<!-- debug logging for log4jdbc itself -->
<logger name="log4jdbc.debug" additivity="false">
<level value="DEBUG" />
<appender-ref ref="console" />
<appender-ref ref="rollingFile"/>
</logger>
<root>
<level value="DEBUG" />
<appender-ref ref="console" />
<appender-ref ref="rollingFile"/>
</root>
</log4j:configuration>


4. 로그 처리 결과 
 
2008-10-24 10:23:02 [jdbc.audit] - <2. Connection.setAutoCommit(true)
2008-10-24 10:23:02 [java.sql.Connection] - <{conn-100003} Connection>
2008-10-24 10:23:02 [java.sql.Connection] - <{conn-100003}
Preparing Statement:
select count(userid) as total from MEMBER where userid = ?
and password = ? >
2008-10-24 10:23:02 [jdbc.audit] -
<2. Connection.prepareStatement(
select count(userid) as total from MEMBER where
userid = ? and password = ? )
2008-10-24 10:23:02 [jdbc.audit] -
<2. PreparedStatement.setQueryTimeout(0)
2008-10-24 10:23:02 [jdbc.audit] -
<2. PreparedStatement.setString(1, "pepsi")
2008-10-24 10:23:02 [jdbc.audit] -
<2. PreparedStatement.setString(2, "1234")
2008-10-24 10:23:02 [java.sql.PreparedStatement] -
<{pstm-100004}
Executing Statement:
select count(userid) as total from MEMBER where userid = ? and password = ? >
2008-10-24 10:23:02 [java.sql.PreparedStatement]
- <{pstm-100004}
Parameters: [pepsi, 1234]>
2008-10-24 10:23:02,281 DEBUG [java.sql.PreparedStatement] -
<{pstm-100004} Types: [java.lang.String, java.lang.String]>
2008-10-24 10:23:02,281 DEBUG [jdbc.sqlonly] -
<2. select count(userid) as total from MEMBER where userid = 'pepsi'
and password = '1234' >
2008-10-24 10:23:02,281 DEBUG [jdbc.sqltiming] -
<2. select count(userid) as total from MEMBER where userid = 'pepsi'
and password = '1234'
{executed in 0 msec}>
2008-10-24 10:23:02 [jdbc.audit] - <2. PreparedStatement.execute()
2008-10-24 10:23:02 [jdbc.audit] - <2. PreparedStatement.getResultSet()
2008-10-24 10:23:02 [java.sql.ResultSet] - <{rset-100005} ResultSet>
2008-10-24 10:23:02 [jdbc.resultset] - <2. ResultSet.getType()
2008-10-24 10:23:02 [jdbc.resultset] - <2. ResultSet.next() returned true
2008-10-24 10:23:02 [jdbc.resultset] - <2. ResultSet.getMetaData() returned
2008-10-24 10:23:02 [jdbc.resultset] - <2. ResultSet.getInt(TOTAL) returned 1
2008-10-24 10:23:02 [jdbc.resultset] - <2. ResultSet.wasNull() returned false
2008-10-24 10:23:02 [jdbc.resultset] - <2. ResultSet.next() returned false
2008-10-24 10:23:02, [java.sql.ResultSet] - <{rset-100005} Header: [TOTAL]>
2008-10-24 10:23:02 [java.sql.ResultSet] - <{rset-100005} Result: [1]>
2008-10-24 10:23:02 [jdbc.audit] - <2. Connection.getMetaData()
2008-10-24 10:23:02 [jdbc.resultset] - <2. ResultSet.close()
2008-10-24 10:23:02 [jdbc.audit] - <2. PreparedStatement.close()
2008-10-24 10:23:02 [jdbc.audit] - <2. Connection.isClosed()
2008-10-24 10:23:02 [jdbc.audit] - <2. Connection.getAutoCommit()
2008-10-24 10:23:02 [jdbc.audit] - <2. Connection.clearWarnings()
2008-10-24 10:23:02 [jdbc.audit] - <2. Connection.setAutoCommit(true)

jdbc.sqltiming 로그에 보면 실행된 FULL Query가 보일 것입니다.
 
[출처] : http://mimul.com/pebble/default/2008/10.html

'DEVELOPMENT > Java' 카테고리의 다른 글

Quartz 문서  (0) 2011.07.13
[JAVA] 동적 캐스팅  (0) 2011.06.10
Commons-lang, Commons-io 사용 샘플  (0) 2011.06.01
JAVA Tip  (0) 2011.04.13
JNI 라이브러리 파일의 경로 동적 설정  (0) 2011.02.25

JAVA Tip

DEVELOPMENT/Java2011. 4. 13. 10:34

1. 띄워쓰기 공백 제거

문자열.replaceAll(\\p{Space}, "");

참고 : http://download.oracle.com/javase/6/docs/api/java/util/regex/Pattern.html


JNI를 사용하여 c 함수를 호출할 때의 실제 라이브러리 파일을 로딩하기 위하여 다음과 같이한다.

  1. 환경 변수 PATH에 포함된 디렉토리에 dll 파일을 카피해 둔다.
  2. java 실행 시 -Djava.library.path=c:\library\path 와 같이 dll 파일이 있는 디렉토리를 설정한다.

그런데 위 방법들이 마땅치 않을 수 있다. 웹 어플리케이션의 경우 필요한 파일 모두를 해당 웹 어플리케이션 디렉토리 밑에 모두 모아두고 싶은 경우이다. 만약 1번의 방법을 사용한다면 해당 dll만 웹하고는 전혀 관계없는 C:\windows 같은 시스템 폴더에 두어야 한다. 혹은 dll이 있는 디렉토리 이름을 환경변수 PATH에 포함시켜야 한다. 역시 깔끔하지 않다. 2번 째 방법을 적용하려면 WAS의 실행 스크립트를 수정하여야 한다. 특정 웹 어플리케이션을 설치하기 위하여 WAS 자체를 건드리는 것은 바람직하지 않다.

실행 시에 System 속성 "java.library.path"를 설정하고, 그 변경된 것이 적용되면 좋겠는데, 실행전에 설정하지 않으면 적용되지 않는다.

java.lang.ClassLoader의 코드를 보면 "java.library.path"에 설정된 값을 초기에 한번 String[] usr_paths에 담아두고 그것을 계속 사용한다. reflection을 사용하여 usr_paths에 원하는 경로를 추가하게 하면 되겠다. 하지만 이런 방법은 바람직하진 않고, 또 어떤 위험성이 있는지도 추측하기 힘든 꼼수다.

아래 코드를 달긴 하지만, 어떤 위험성이 있는지는 아직 파악되지 않았다.
그리고 ClassLoader의 private 속성 usr_paths의 이름이 각 JVM마다 다 똑같다는 것도 확인 안되었고.
무엇 보다도 치명적인 것은 private으로 은닉화시켜 놓은 것은 언제든지 변경될 수 있고, 유지보수가 어렵다는 것이다.
꼼수다운 단점이다.

 public static void loadLibrary(String libraryPath, String libraryName) throws LibraryLoadingException {
     
  // 라이브러리 패스를 추가
     addLibrarayPath(libraryPath);
  // 라이브러리를 로딩    
     System.loadLibrary(libraryName);
     
    }

 private static void addLibrarayPath(String libraryPath) throws LibraryLoadingException {
  
  if(libraryPath==null) { return; }
  if(libraryPath.equals("")) { return; }
  
     Field usrPathsField;
  // ClassLoader의 String[] usr_paths 필드를 구하고
  // usr_paths의 값은 시스템 속성 java.library.path의 값으로 초기에 한번만 설정된다.
  try {
   usrPathsField = getField(ClassLoader.class, "usr_paths");
  } catch (ReflectionFailException e) {
   throw new LibraryLoadingException("library loading failed.", e);
  }

  if(usrPathsField==null) {
   throw new LibraryLoadingException("library loading failed. invalid attribute usr_paths");
  }

  // private으로 선언된 것을 접근 가능하게 바꿔주고.
     usrPathsField.setAccessible(true);
     
     String[] userPaths;
  try {
   // 실제 설정된 값을 구하고
   userPaths = (String[])usrPathsField.get(null);
  } catch (IllegalArgumentException e) {
   throw new LibraryLoadingException("library loading failed.", e);
  } catch (IllegalAccessException e) {
   throw new LibraryLoadingException("library loading failed.", e);
  }
     
  // 만약 추가할 패스가 이미 있다면 더 할일이 없다. 나간다.
     for(int i=0; i<userPaths.length; i++) {
      if(libraryPath.equalsIgnoreCase(userPaths[i])) {
       return;
      }
     }

  // 새로운 패스를 추가하고     
     String[] newUserPaths = new String[userPaths.length+1];
     for(int i=0; i<userPaths.length; i++) {
      newUserPaths[i] = userPaths[i];
     }
     newUserPaths[newUserPaths.length-1] = libraryPath;
     
  // 추가된 패스가 포함된 새로운 값을 설정한다.
     try {
   usrPathsField.set(null, newUserPaths);
  } catch (IllegalArgumentException e) {
   throw new LibraryLoadingException("library loading failed.", e);
  } catch (IllegalAccessException e) {
   throw new LibraryLoadingException("library loading failed.", e);
  }
     
 }


 public static Field getField(Class clazz, String fieldName) throws ReflectionFailException {
  
  if(clazz==null) { return null; }
  if(fieldName==null) { return null; }
  
  Field field = null;
  
  Exception rootException = null;
  try {
   field = clazz.getDeclaredField(fieldName);
   return field;
  } catch (SecurityException e) {
   rootException = e;
  } catch (NoSuchFieldException e) {
   rootException = e;
  }
  
  Class superClass = clazz.getSuperclass();
  while(superClass!=null) {
   try {
    field = superClass.getDeclaredField(fieldName);
    return field;
   } catch (SecurityException e) {
   } catch (NoSuchFieldException e) {
   }
   superClass = superClass.getSuperclass();
  }
  
  throw new ReflectionFailException("getting field "+fieldName+" failed.", rootException);
 }

 


2009/05/11 추가사항

이외에 다른 방법이 있다. System.loadLibrary()는 시스템 속성 "java.library.path"의 값을 사용하기 전에 메소드를 호출한 클래스의 클래스로더의 findLibrary(String libraryName)을 호출한다. 해당 라이브러리의 경로를 반환하는 메소드 이다. 만약 요 메소드의 반환값을 적절히 해주면 해결될 것이다. 가장 합벅적인(?)인 방법이다. 그러나 클래스 로드를 따로 개발해야 한다는 부담이 있다. 더군다나 native 라이브러리를 호출하는 코드를 건드리지 못한다면(다른 곳에서 가져온 라이브러리일 경우) 요 방법은 적용하지 못한다. 실제로 테스트해본 결과 로딩은 되긴 한다. 그러나 라이브러리간의 의존성에서 실패한다. a.dll이 있고 b.dll이 있고, a.dll이 b.dll을 사용하는 경우이다. java 코드에서는 a.dll을 잘 로딩했는데 a.dll이 b.dll을 로딩하지 못하였다. "Can't find dependent libraries" 메시지의 java.lang.UnsatisfiedLinkError가 발생한다. 이러한 것이 내가 작성한 코드의 버그인지 다른 원인인지도 모르는 상태에서 덮어 버렸다.

혹시나 해서 비슷한 코드를 찾아 보았다. theyard라는 프로젝트에 JNILibraryLoader라는 클래스가 있다.(http://code.google.com/p/theyard/source/detail?r=168). 가져다 사용해 보니 훌륭하게 동작한다. 내부를 들여다 보니 System.load(String fileName)을 사용하고 있다. 요메소드가 System.loadLibrary()와 무슨 차이가 있는지는 파악하진 않았다. System.load()가 실패하면 loadLibrary()를 호출한다. 그리고 더 마음에 드는 것은 OS별 라이브러리 파일의 확장자를 알아서 처리해 주고, 라이브러리의 버전까지도 명시할 수 있다. 실제 수정한 코드를 첨부한다. JniLibraryLoader.java 사용방법은 다음과 같다.
JniLibraryLoadder.load("c:/some/path", "myLib");


System.loadLibrary()메소드가 시스템 속성 "java.library.path"의 값을 그때 마다 읽지 않고 한번만 읽고는 그 값을 사용하게 한 것은 무언가 이유가 있을 것 같다. 뭔지는 잘 모르겠는데, 이렇게 처리하는 것이 그 무언지 모르는 위험성을 안고 가는것 같아 찜찜하다.

[출처] http://aploit.egloos.com/4937154

'DEVELOPMENT > Java' 카테고리의 다른 글

Commons-lang, Commons-io 사용 샘플  (0) 2011.06.01
JAVA Tip  (0) 2011.04.13
Velocity의 기본 문법  (0) 2011.02.11
Spring - Quartz를 사용하여 스케쥴러 구현하기  (0) 2011.02.08
EHCache를 이용한 캐시 구현  (0) 2011.01.05


가끔 서버에서 주기적으로 어떠한 작업을 하고자 할때 리눅스에서는 크론탭을 사용하여 주기적으로 어떠한 작업을 처리합니다.
이런 주기적 작업을 처리하기위해 Spring에서 지원해 주는 Quartz스케쥴러를 통해 크론탭과 같은 역할을 하는 스케쥴러를 작성할 수 있습니다.
이번에는 Spring 과 Quartz를 연동하여 스케줄러를 작성해 보겠습니다.

작업순서는
스프링 기본 세팅 -> Quartz 세팅 순으로 작업하겠습니다.

1. 스프링 기본 설정
1) springframework.org 로 이동하셔서 스프링 라이브러리를 다운 받습니다.

위와 같은 페이지가 뜨면 해당사항을 입력하시고 Access Download 를 클릭하셔서 다운로드 페이지로 이동합니다. (귀찮으신 분들은 하단의 파란색으로 "download page"를 선택하시면 입력하시지 않고도 다운로드 페이지로 이동하실수 있습니다.

많은 버전의 라이브러리 중 spring-framework-2.5.6.SEC02.zip 를 다운 받습니다. 다른 버전을 다운 받으셔도 상관없습니다만 버전에 따라 세팅 내용이 조금 씩 달라지므로 같은 버전의 라이브러리로 진행하는 것이 나을 것같네요~^^.

2) 이렇게 라이브러리까지 다운로드 받고 나면 Eclipse와 같은 IDE에서 Dynamic Web Project를 선택하여 Project를 한개 생성합니다.
(저는 SpringQuartz 라는 이름으로 생성했습니다.)

3) 프로젝트가 생성되면 프로젝트 안에 /WEB-INF/lib 디렉토리에 스프링 라이브러리를 압축 푼 곳에 있는 dist/spring.jar 파일을 추가합니다.
* 팁 : 프로젝트를 진행하다 보면 위와같이 라이브러리 버전이 없는 jar파일을 그냥 추가하는 경우가 있는데 나중에 라이브러리를 업데이트 해야 할일이 생기게 되면 위와같이 spring.jar 라고 되어있으면 지금 적용되어 있는 버전이 몇 인지 알수가 없습니다. 그렇기 때문에 항상 라이브러리 추가하실때는 추가하시는 라이브러리의 버전 번호를 파일이름 뒤에 추가하는 습관을 들이 시는게 좋습니다.
     ex) spring-2.5.6.jar

4) 프로젝트 안에 생성된 web.xml에 Spring을 사용하기 위한 세팅을 추가해 줍니다.
* 저는 Quartz를 사용하기 위한 최소한의 Spring 세팅을 해놓았기 때문에 세팅 내용이 단순합니다. 만약 웹프로젝트와 함께 Quartz를 사용하신다면 웹에 맞게 설정하시고 사용하셔야 함을 알려드립니다.^^
  1. <?xml version="1.0" encoding="UTF-8"?>  
  2. <web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"  
  3.     xmlns="http://java.sun.com/xml/ns/javaee"      
  4.     xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"   xsi:schemaLocation="http://java.sun.com/xml/ns/javaee      
  5.     http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"  id="WebApp_ID" version="2.5">    <listener>  
  6.         <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>     
  7.     </listener>      
  8.     <context-param>          
  9.         <param-name>contextConfigLocation</param-name>         
  10.         <param-value>/WEB-INF/config/applicationContext*.xml</param-value>  
  11.     </context-param>  
  12. </web-app>  


5) 쿼츠 라이브러리를 다운로드 받고 라이브러리를 추가해 줍니다.
쿼츠 라이브러리 다운로드 하신다음 압축을 풀어 줍니다.
해당 라이브러리를 프로젝트의 lib 디렉토리에 복사하여 넣어줍니다.
- quartz-all-1.8.3.jar
- 압축푼 lib 디렉터리의 log4j-1.2.14.jar
- 압축푼 lib 디렉터리의 slf4j-api-1.5.10.jar
- 압축푼 lib 디렉터리의 slf4j-log4j12-1.5.10.jar
를 추가 해 줍니다.
마지막으로 apache의 commons-logging-1.1.1.jar 를 다운로드 하셔서 위와 같이 프로젝트의 lib에 추가해주시면 라이브러리 추가는 끝이 납니다.

6) Quartz의 핵심적인 기능을 할 /WEB-INF/config/applicationConext.xml 을 작성합니다.
스케쥴러의 핵심 세팅은 3가지 정도 입니다.
하나. 실제 주기적으로 실행될 클래스 등록
둘. 스케줄러가 동작하는 interval time 설정
셋. 실제 동작하게 끔 설정

이런 세가지가 있겠습니다.

스케줄러 동작방식에는 두가지가 존재 합니다.
-Simple : interval time이 간단하게 동작하는 방식으로 몇초, 혹은 몇분, 몇시간 단위로 작동하고 싶을때 사용합니다.
<Simple type setting>
  1. <?xml version="1.0" encoding="UTF-8" ?>  
  2. <beans xmlns="http://www.springframework.org/schema/beans"  
  3.        xmlns:context="http://www.springframework.org/schema/context"  
  4.        xmlns:p="http://www.springframework.org/schema/p"  
  5.        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"  
  6.        xsi:schemaLocation="http://www.springframework.org/schema/beans      
  7.                            http://www.springframework.org/schema/beans/spring-beans-2.5.xsd   
  8.                            http://www.springframework.org/schema/context   
  9.                            http://www.springframework.org/schema/context/spring-context-2.5.xsd">  
  10.     <!-- 하나.주기적으로 실행될 클래스 설정 -->  
  11.     <!-- property name은 jobClass로 fix, value는 사용자가 작성한 class 파일 위치 -->  
  12.     <bean id="simpleQuartzJob" class="org.springframework.scheduling.quartz.JobDetailBean">  
  13.         <property name="jobClass" value="net.test.quartz.SimpleQuartzJob"/>  
  14.     </bean>  
  15.   
  16.     <!-- 둘.스케줄러의 interval time 설정 -->  
  17.     <!-- 쿼츠에는 아래와 같이 몇초에 한번씩 돌게 하는 Simple type 과 -->  
  18.     <!-- 무슨 요일 몇시에 한번씩 돌게 하는 날짜로 지정하는 Cron type 이 있다. -->  
  19.     <!-- 현재는 Simple type으로 세팅 -->  
  20.     <!-- jobDetail은 위에서 설정한 실제 동작할 클래스 id를 적어준다 -->  
  21.     <!-- startDelay는 서버 시작후 몇초 뒤에 시작할지 세팅(ms 단위)  -->  
  22.     <!-- repeatInterval은 몇 초에 한번씩 실행될 건지 세팅(ms 단위: 현재 1초) -->  
  23.     <bean id="simpleTrigger" class="org.springframework.scheduling.quartz.SimpleTriggerBean">  
  24.         <property name="jobDetail" ref="simpleQuartzJob"/>  
  25.         <property name="startDelay" value="1000"/>  
  26.         <property name="repeatInterval" value="1000"/>  
  27.     </bean>  
  28.     <!--셋. 실제 동작하게끔 설정 -->  
  29.     <!--ref bean은 위에서 설정한 interval time 아이디를 넣어주면 됨  -->  
  30.     <bean class="org.springframework.scheduling.quartz.SchedulerFactoryBean">  
  31.         <property name="triggers">  
  32.             <list>  
  33.                 <ref bean="simpleTrigger"/>  
  34.             </list>  
  35.         </property>  
  36.         <!-- Quartz 실행시 세팅 -->  
  37.         <property name="quartzProperties">  
  38.             <props>  
  39.                 <prop key="org.quartz.threadPool.class">org.quartz.simpl.SimpleThreadPool</prop>  
  40.                 <prop key="org.quartz.threadPool.threadCount">5</prop>  
  41.                 <prop key="org.quartz.threadPool.threadPriority">4</prop>  
  42.                 <prop key="org.quartz.jobStore.class">org.quartz.simpl.RAMJobStore</prop>  
  43.                 <prop key="org.quartz.jobStore.misfireThreshold">60000</prop>  
  44.             </props>  
  45.         </property>  
  46.     </bean>  
  47. </beans>  

-Cron : linux 의 Cron tab 과 같은 역할을 하는 타입니다. 즉 몇월, 몇일 몇시에 동작하게 하고 싶으면 Cron type을 사용하시면 됩니다.
<Cron type setting>
  1. <?xml version="1.0" encoding="UTF-8" ?>  
  2. <beans xmlns="http://www.springframework.org/schema/beans"  
  3.        xmlns:context="http://www.springframework.org/schema/context"  
  4.        xmlns:p="http://www.springframework.org/schema/p"  
  5.        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"  
  6.        xsi:schemaLocation="http://www.springframework.org/schema/beans      
  7.                            http://www.springframework.org/schema/beans/spring-beans-2.5.xsd   
  8.                            http://www.springframework.org/schema/context   
  9.                            http://www.springframework.org/schema/context/spring-context-2.5.xsd">  
  10.     <!--하나. 주기적으로 실행될 클래스 설정 -->  
  11.     <bean id="cronQuartzJob" class="org.springframework.scheduling.quartz.JobDetailBean">  
  12.         <property name="jobClass" value="net.test.quartz.CronQuartzJob"/>  
  13.     </bean>  
  14.        
  15.     <!--둘. 스케줄러의 interval time 설정-->  
  16.     <!--cronExpression을 통해서 스캐줄러 주기를 설정한다. -->  
  17.     <bean id="cronTrigger" class="org.springframework.scheduling.quartz.CronTriggerBean">  
  18.         <property name="jobDetail" ref="cronQuartzJob"/>  
  19.         <property name="cronExpression" value="0/1 * * * * ?"/>  
  20.     </bean>  
  21.        
  22.     <!--셋. 실제 동작하게끔 설정 -->  
  23.     <bean class="org.springframework.scheduling.quartz.SchedulerFactoryBean">  
  24.         <property name="triggers">  
  25.             <list>  
  26.                 <ref bean="cronTrigger"/>  
  27.             </list>  
  28.         </property>  
  29.         <property name="quartzProperties">  
  30.             <props>  
  31.                 <prop key="org.quartz.threadPool.class">org.quartz.simpl.SimpleThreadPool</prop>  
  32.                 <prop key="org.quartz.threadPool.threadCount">5</prop>  
  33.                 <prop key="org.quartz.threadPool.threadPriority">4</prop>  
  34.                 <prop key="org.quartz.jobStore.class">org.quartz.simpl.RAMJobStore</prop>  
  35.                 <prop key="org.quartz.jobStore.misfireThreshold">60000</prop>  
  36.             </props>  
  37.         </property>  
  38.     </bean>  
  39. </beans>  


Cron type을 사용하려면 CronExpression을 알아야 합니다.

*Cron Expression
cron expression의 각각의 필드는 다음을 나타낸다.(왼쪽 -> 오른쪽 순)
 필드 이름  허용 값  허용된 특수 문자
 Seconds  0 ~ 59  , - * /
 Minutes  0 ~ 59  , - * /
 Hours  0 ~ 23  , - * /
 Day-of-month  1 ~ 31  , - * ? / L W
 Month  1 ~12 or JAN ~ DEC   , - * /
 Day-Of-Week  1 ~ 7 or SUN-SAT  , - * ? / L #
 Year (optional)  empty, 1970 ~ 2099  , - * /

Cron Expression 의 특수문자
'*' : 모든 수를 나타냄. 분의 위치에 * 설정하면 "매 분 마다" 라는 뜻.
'?' : day-of-month 와 day-of-week 필드에서만 사용가능. 특별한 값이 없음을 나타낸다.
'-' : "10-12" 과 같이 기간을 설정한다. 시간 필드에 "10-12" 이라 입력하면 "10, 11, 12시에 동작하도록 설정" 이란 뜻.
',' : "MON,WED,FRI"와 같이 특정 시간을 설정할 때 사용한다. "MON,WED,FRI" 이면 " '월,수,금' 에만 동작" 이란 뜻.
'/' : 증가를 표현합니다. 예를 들어 초 단위에 "0/15"로 세팅 되어 있다면 "0초 부터 시작하여 15초 이후에 동작" 이란 뜻.
'L' : day-of-month 와 day-of-week 필드에만 사용하며 마지막날을 나타냅. 만약 day-of-month 에 "L" 로 되어 있다면 이번 달의 마지막에 실행하겠다는 것을 나타냄.
'W' : day-of-month 필드에만 사용되며, 주어진 기간에 가장 가까운 평일(월~금)을 나타낸다. 만약 "15W" 이고 이번 달의 15일이 토요일이라면 가장가까운 14일 금요일날 실행된다. 또 15일이 일요일이라면 가장 가까운 평일인 16일 월요일에 실행되게 된다. 만약 15일이 화요일이라면 화요일인 15일에 수행된다.
"LW" : L과 W를 결합하여 사용할 수 있으며 "LW"는 "이번달 마지막 평일"을 나타냄
"#" : day-of-week에 사용된다. "6#3" 이면 3(3)번째 주 금요일(6) 이란 뜻이된다.1은 일요일 ~ 7은 토요일 

 Expression  Meaning
 "0 0 12 * * ?"  매일 12시에 실행
 "0 15 10 ? * *"  매일 10시 15분에 실행
 "0 15 10 * * ?"  매일 10시 15분에 실행
 "0 15 10 * * ? *"  매일 10시 15분에 실행
 "0 15 10 * * ?  2010"   2010년 동안 매일 10시 15분에 실행
 "0 * 14 * * ?"  매일 14시에서 시작해서 14:59분 에 끝남
 "0 0/5 14 * * ?"  매일 14시에 시작하여 5분 간격으로 실행되며 14:55분에 끝남
 "0 0/5 14,18 * * ?"  매일 14시에 시작하여 5분 간격으로 실행되며 14:55분에 끝나고, 매일 18시에 시작하여 5분간격으로 실행되며 18:55분에 끝난다.
 "0 0-5 14 * * ?"  매일 14시에 시작하여 14:05 분에 끝난다.

* 시간에 맞춰 돌아가는 스케줄러에서 다른 클래스를 사용하고 싶을 때는 다음과 같이 설정합니다.
  1. <!-- 스프링 DI : 사용할 Service 객체를 생성 -->  
  2. <bean id="quartzJobService" class="net.test.quartz.service.impl.QuartzJobServiceImpl"/>  
  3.   
  4. <bean id="simpleQuartzJob" class="org.springframework.scheduling.quartz.JobDetailBean">  
  5.     <property name="jobClass" value="net.test.quartz.SimpleQuartzJob"/>  
  6.     <!-- 사용하고자 하는 class의 bean id를 등록 -->  
  7.     <property name="jobDataAsMap">  
  8.         <map>  
  9.             <entry key="quartzJobService">  
  10.                 <ref local="quartzJobService"/>  
  11.             </entry>  
  12.         </map>  
  13.     </property>  
  14. </bean>  

 *두가지 스케줄러를 동시에 실행 시킬때
  1. <bean class="org.springframework.scheduling.quartz.SchedulerFactoryBean">  
  2.     <property name="triggers">  
  3.         <!--트리거를 두개 생성후 아래와 같이 세팅 -->  
  4.         <list>  
  5.             <ref bean="simpleTrigger"/>  
  6.             <ref bean="cronTrigger"/>  
  7.         </list>  
  8.     </property>  
  9.     <property name="quartzProperties">  
  10.         <props>  
  11.             <prop key="org.quartz.threadPool.class">org.quartz.simpl.SimpleThreadPool</prop>  
  12.                 <prop key="org.quartz.threadPool.threadCount">5</prop>  
  13.                 <prop key="org.quartz.threadPool.threadPriority">4</prop>  
  14.                 <prop key="org.quartz.jobStore.class">org.quartz.simpl.RAMJobStore</prop>  
  15.                 <prop key="org.quartz.jobStore.misfireThreshold">60000</prop>  
  16.         </props>  
  17.     </property>  
  18. </bean>  


7) 실제 작동할 Class파일 생성
  1. package net.test.quartz;   
  2.   
  3. import net.test.quartz.service.QuartzJobService;   
  4.   
  5. import org.quartz.JobExecutionContext;   
  6. import org.quartz.JobExecutionException;   
  7. import org.springframework.scheduling.quartz.QuartzJobBean;   
  8.   
  9. public class SimpleQuartzJob extends QuartzJobBean{   
  10.     //실행될 클래스는 꼭 QuartzJobBean을 상속받아야 되며    
  11.     //executeInternal method를 override 하면 자동으로 이 메소드가 실행   
  12.   
  13.     //Spring의 DI를 사용하여 Service객체를 setting   
  14.     //DI를 사용하지 않는다면 필요 없는 부분   
  15.     private QuartzJobService quartzJobService;   
  16.     public void setQuartzJobService(QuartzJobService quartzJobService) {   
  17.         this.quartzJobService = quartzJobService;   
  18.     }   
  19.   
  20.   
  21.     @Override  
  22.     protected void executeInternal(JobExecutionContext ex)throws JobExecutionException {   
  23.         quartzJobService.printLog();   
  24.     }   
  25.        
  26. }  


위와 같은 방식으로 Spring과 Quartz를 사용하여 스케줄러를 사용할 수 있습니다.

함께 업로드 하는 파일은 제가 직접 작업한 프로젝트이구요. 함께 확인 해 보시면 쉽게 쿼츠를 사용하실수 있으실 겁니다.~^^



출처 : http://javastore.tistory.com/96