오픈API 등을 이용하려면 XML을 파싱해서 JavaFX로 가져와야 합니다.
HttpRequest를 이용해서 xml을 요청한 다음에 PullParser를 이용해 한줄한줄 파싱해서 데이터로 가져오는 방식을 사용합니다.

음...JavaFX가 1.2로 업데이트 되었는데요. 기존에 HttpRequest에서 요청할 때 enqueue()라는 함수로 실행을 했는데, start()로 함수명이 바뀌었네요. start()가 깔끔하군요.

간단하게 네이버OpenAPI를 파싱해보겠습니다.
실시간 급상승 검색어를 간단히 뿌려주는 소스입니다.
사용자 삽입 이미지

main.fx
[code]package xmlparser;

import javafx.stage.Stage;
import javafx.scene.Scene;
import javafx.scene.text.Text;
import javafx.scene.text.Font;

var rankingInfo:RankingInfo = RankingInfo{
    onDone: function() {
        var content:String = "";
        for (item in rankingInfo.ranking) {
            content += "{item}\n";
        }
        text.content = content;
    }
};

var text:Text = Text {
    font: Font {
        size: 16
    }
    x: 10
    y: 30
}

Stage {
    title: "Application title"
    width: 250
    height: 300
    scene: Scene {
        content: text
    }
}[/code]
RankingInfo.fx
[code]package xmlparser;

import javafx.data.pull.PullParser;
import javafx.io.http.HttpRequest;
import java.lang.Exception;

public class RankingInfo {
    var url:String = "http://openapi.naver.com/search?key=네이버OpenAPI키&target=rank&query=nexearch";
    var p:PullParser;
    var h:HttpRequest;
    public var ranking:String[];
    public var onDone:function() = null;
   
    init {
        ranking = [];
        h = HttpRequest {
            location: url
            onException: function(exception:Exception) {
                exception.printStackTrace();
            }
            onInput: function(input) {
                var i;
                p = PullParser {
                    documentType: PullParser.XML
                    input: input
                    onEvent: function(event) {
                        if (event.type == PullParser.START_DOCUMENT) {
                            ranking = [];
                            i = 0;
                        }
                        else if (event.type == PullParser.END_ELEMENT
                            and event.level == 3) {
                           if (event.qname.name == "K") {
                               println("{event.text}");
                               ranking[i] = event.text;
                               i++;
                           }
                        }
                        else if (event.type == PullParser.END_DOCUMENT) {
                            onDone();
                        }
                    }
                }
                p.parse();
                p.input.close();
            }
        }
        h.start();
    }
}
[/code]
PullParser와 HttpRequest를 이용합니다. HttpRequest를 이용해서 url을 지정해서 가져오면 onInput이 발생합니다. 여기에서 input을 PullParser에 지정을 해주면 한줄씩 읽을 때마다 onEvent가 발생하게 됩니다.
onEvent에서는 한줄씩 읽으면서 Parsing을 해주면 됩니다.
해당 엘리먼트를 가져오기위해선 문서의 레벨과 태그의 이름으로 알 수 있습니다.
[code]<result>
<item>
<R1>
<K>투시안경</K>
<S>+</S>
<V>105</V>
</R1>
</item>
</result>[/code]
위와 같은 xml이라면 <K>값을 가져오기 위해서는 K의 레벨과 K를 알면 됩니다. K레벨은 result를 0, item을 1, R1을 2, K는 3이 됩니다.
[code]if (event.type == PullParser.END_ELEMENT and event.level == 3) {
if (event.qname.name == "K") {
    println("{event.text}");
}[/code]
3이고, qname.name이 K인걸 찾으면 돼요. END_ELEMENT에서 해야하는 이유는 START_ELEMENT에서하면 값이 아직 파싱이 안된 상태여서 그렇습니다-_-

JSON인 경우도 비슷해요.
단지 Pullparser.START_ELEMENT나 END_ELEMENT가 아닌, END_VALUE로 파악을 하면 됩니다.
documentType: PullParser.JSON으로 바꿔주셔야 해요.
 
Posted by 머드초보
,
 
음...아직 JavaFX는 삽질할 것이 못되는군요. 얼마전 세미나에서 실버라이트1.0때 열악함을 말해줬는데 그것이 생각나는군요. JavaFX가 지금 현재 열악함 상태입니다.

우선 이 예제는 ManiaDB의 OpenAPI를 이용한 나름(?) 매쉬업 애플리케이션입니다 ㅠ 해당 가수명을 입력하면 매니아디비에서 앨범목록을 가져와서 앨범이미지를 DisplayShelf형태로 뿌려주는 애플리케이션입니다.

여러가지 문제점이 있었는데요ㅠ

1. JavaFX HttpRequest, PullParser 문제점

이 놈을 알아야지 API데이터를 가져올 수 있습니다. 이걸 사용하면서 뭔가 문제점이 있다면...XML파싱할 때 복잡합니다. Flex의 HTTPService는 축복입니다-_- 그리고, XML의 값이 없는 경우(ex:<description><![CDATA[]]></description>)에는 nullpointException을 내뿜으며 파싱에러를 냅니다-_- 매니아디비는 description이 없는 앨범이 있어서 저런 에러를 내뿜더군요.
그래서 꼼수(?)를 사용했습니다. JSON으로 변환해서 쓰면 됩니다-_- JSON은 에러를 안뿜어요. JSON으로 바꾸려면 역시 야후파이프가 짱-_-
야후파이프주소 : http://pipes.yahoo.com/pipes/
앨범검색파이프 : http://pipes.yahoo.com/pipes/pipe.info?_id=5e24977627eb6c03b1d7e9aeb820aca7
앨범검색파이트에서 Get as JSON으로 받으시면 JSON으로 받을 수 있습니다. 이걸가지고 HttpRequest클래스를 이용해서 파싱하면 됩니다.

파싱할 때 XML같은 경우에는 ELEMENT명과 LEVEL로 구분을 해서 값을 가져올 수 있습니다. PullParser라는 클래스를 이용해서 파싱하는데, XML엘리먼트마다 onEvent함수가 발생합니다. 거기서 해당 엘리먼트명과 level(얼마나 깊이 들어갔는지-_-)를 if문으로 구분해서 맞으면 값을 가져오는 그런 식으로 파싱합니다-_-
JSON도 비슷합니다. 예를 들어 값이 끝날 때 "로 끝나면 END_VALUE인지 확인 후 원하는 이름인지 확인 후 가져오면 됩니다. 생각해보니...뭐 그리 복잡하지도 않군요.


2. 과도한 {}로 불편한 문법-_-

조금 하다보니 이제 익숙해지려고 하는 듯하면서도 헷깔리는 과도한 {}-_- 이건 JSON도 아니고, Javascript도 아니고, Java도 아닌데, 3가지 문법이 짬뽕이 되었습니다. 재미있는 건 Java클래스를 그대로 쓸 수 있는 듯합니다. 어느정도인지는 안해봤는데, URI를 encode해야해서 어떻게 해야하나 생각하던중에 Java클래스에 있는 URIEncoder를 사용하니 그냥 되더군요-_- 예전에 선테크데이에서도 Java를 임포트할 수 있다고 들은 것 같아서^^
이 문법도 계속 보니....적응이 되는군요.

그 외에 문제가 굉장히 많았는데, 기억이 안나네요-_-

사용자 삽입 이미지

이건 스크린샷이에요-_- 낚이지 마세요-_-

실행예제 http://mudchobosample.appspot.com/AlbumSearch/AlbumSearch.html
실행 시 신뢰할꺼냐고 물어보는데, 신뢰한다고 해야 실행됩니다 ㅠ 외부파일을 끌어쓰기에..-_- 그리고 자기 인증서기때문에 6개월 뒤에 만기된다고 합니다 ㅠㅠ 인증서를 사야지 JavaFX를 제대로 쓸 수 있는 듯 합니다.

소스코드입니다.
http://my-svn.assembla.com/svn/mudchobosample/trunk/AlbumSearch/


참고자료
http://www.javafx.com/samples/InterestingPhotos/index.html
http://www.javafx.com/samples/DisplayShelf/index.html

PS. 활성화가 로컬에서 할 때에는 잘 되는데, 올리니까 안되네요. 그래서 TextInput부분을 강제로 활성화를 시켜버렸습니다ㅠ Swing컴포넌트를 써서 그런가....
 
Posted by 머드초보
,
 
구글 앱 엔진이 자바를 지원하기 시작했습니다.
전 세계적으로 가장 인기있는(?) 언어인 자바를 지원하다니 대단합니다-_-(사실...파이썬 보다 자바나 PHP를 먼저 지원했어야 했을 것 같은데-_- 그때 얘기를 들어보니 앱엔진 담당하는 사람이 파이썬 전문가라는 얘기를 들은 것 같군요-_-)

거기에 더 대단한 것은 파이썬 같은 경우에는 툴 같은 것이 없었는데, 이번엔 이클립스를 이용한 툴까지 제공해줍니다. 구글플러그인인데요. App Engine JAVA SDK랑 덤으로 Web Toolkit SDK까지 깔아주는군요. 게다가 구글 앱엔진에 쉽게 배포까지 할 수 있습니다.

이클립스 설치주소 : http://dl.google.com/eclipse/plugin/3.4 (3.3버전은 뒤에 3.3-_-)
참조문서 : http://code.google.com/intl/ko-KR/appengine/docs/java/tools/eclipse.html

설치를 하게 되면 File -> New -> Other에 보시면 Google이라는 폴더에 Web Application Project라는 프로젝트가 있습니다. 이걸 선택하게 되면 아래와 같은 화면이 나옵니다.
사용자 삽입 이미지
Web Toolkit은 체크를 안해도 됩니다.

프로젝트가 완성이 되면 익숙한(?) 폴더구조를 볼 수 있습니다. src는 java소스부분이 들어가는 곳이고, war부분은 WebContent부분이군요. 안에 더욱 익숙한 WEB-INF도 있어요-_- 라이브러리 마구 처박아도 되나-_- web.xml도 있군요.

자 그럼, 손쉬운 배포를 해봅시다. 갓난애기도 할 수 있는 앱엔진 배포!(과연..-_-)
war -> WEB-INF폴더에 appengine-web.xml파일을 엽니다.
[code]<?xml version="1.0" encoding="utf-8"?>
<appengine-web-app xmlns="http://appengine.google.com/ns/1.0">
    <application>mudchobo</application>
    <version>1</version>
   
    <!-- Configure java.util.logging -->
    <system-properties>
        <property name="java.util.logging.config.file" value="WEB-INF/logging.properties"/>
    </system-properties>
   
</appengine-web-app>[/code]
<application>부분에 자신이 애플리케이션 생성 시 입력한 Application Identifier를 입력합니다.

그리고, 프로젝트에 대고, 마우스오른쪽 버튼을 누르면 Google이라는 메뉴가 있습니다.
Deploy to App Enigne이라는 메뉴를 선택합니다.
사용자 삽입 이미지
자신의 App Engine계정을 입력하면 바로 배포할 수 있습니다.

http://mudchobo.appspot.com/ 여기에 배포가 되었습니다.

Spring 등의 라이브러리도 다 되는거겠죠?-_- 이번 연휴에 삽질을 해봐야겠습니다.

PS. 혹시나 파일의 용량제한이 있나테스트해봤더니 10메가 이상파일은 안올라가네요.
Unable to upload app: Found a file too large to upload: "C:\Users\mudchobo\AppData\Local\Temp\appcfg3803788145857696233.tmp\Aptana Studio.zip".  Must be under 10000000 bytes.
 
Posted by 머드초보
,
 
오픈아이디에서 제공하는 추가정보(이메일, 닉네임, 생일 등)를 얻어오려면 Simple Registration Extension를 이용해서 가져와야합니다. (OpenID4java에 들어있는 Servlet예제를 보면 Attribute Exchange로 하는데, 이걸로 하면 안되더군요-_-; 그래서 Outsider님의 블로그의 글을 참조를 했습니다.
http://blog.outsider.ne.kr/170

만약 myid.net에 한글 닉네임이 있는 경우에는 가져올 때 URL을 통해서 가져오는데요.
URL이 UTF-8로 되어있고, encodeURI해서 오는데, 이것을 제대로 decode를 못해줘서 인증에 실패를 하더군요.

가져올 때
[code]ParameterList response = new ParameterList(httpReq.getParameterMap());[/code]
이렇게 해서 가져오면 한글인 경우 문제가 발생합니다.
Verification failed for: null reason: null 이런 에러를 발생하는데, 디버깅할 때 parameterList에 있는 한글로 받아야하는 닉네임이 있다면 깨져서 나올겁니다.
그래서 제가 임의로 url을 URIDecoder로 decode를 해서 nickname을 한글로 바꿨더니 인증이 되더군요.
다른 방법을 찾다가 MyID담당자님께 물어봤더니 아래와 같은 방법을 가르쳐주셨습니다 ^^

아래와 같이 파라메터를 얻어오면 됩니다.
[code]ParameterList paramList = ParameterList.createFromQueryString(request.getQueryString());[/code]
이렇게 하니까 되더군요.

알려주신 MyID의 수호이님 감사합니다 ^^

 
Posted by 머드초보
,
 

[NetBeans] 넷빈즈를 이용한 Hibernate 초간단 예제1 - Hibernate 셋팅

에 이어서-_-;

이제 셋팅은 다 된 것 같으니, 이 데이터를 가져와봅시다.
Hibernate에서 쉽게 데이터를 가져올 수 있도록 Helper클래스를 만들어봅시다.

Ctrl + N -> Java -> Java Class -> Class Name은 ScoreHelper, Package는 wondergirlshibernate로 합니다.

ScoreHelper.java
[code]package wondergirlshibernate;

import java.util.List;
import org.hibernate.HibernateException;
import org.hibernate.Query;
import org.hibernate.Session;
import org.hibernate.Transaction;
import wondergirlshibernate.entity.Score;
import wondergirlshibernate.util.HibernateUtil;

public class ScoreHelper {
Session session = null;

    public ScoreHelper() {
        this.session = HibernateUtil.getSessionFactory().openSession();
    }

    public List<Score> getWondergirlsScore() {
        List<Score> wondergirlsScoreList = null;

        try {
            Transaction tx = session.beginTransaction();
            Query q = session.createQuery("from Score");
            wondergirlsScoreList = (List<Score>) q.list();
        } catch (HibernateException e) {
            e.printStackTrace();
        }
        return wondergirlsScoreList;
    }
}
[/code]
getWondergirlsScore를 보면 from Score를 실행하는 것이 다 입니다. 그리고, 그 리스트를 받아오는 거죠.

Main클래스를 봅시다.
Main.java
[code]package wondergirlshibernate;

import java.util.List;
import wondergirlshibernate.entity.Score;

public class Main {

    public static void main(String[] args) {
        ScoreHelper helper = new ScoreHelper();

        List list = helper.getWondergirlsScore();

        System.out.println(
                String.format("%10s %10s %10s %10s", "이름", "나이", "국어", "수학"));
        for (int i = 0; i < list.size(); i++) {
            Score score = (Score) list.get(i);

            System.out.println(
                String.format("%10s %10s %10d %10d",
                    score.getWondergirls().getName(),
                    score.getWondergirls().getAge(),
                    score.getKorean(),
                    score.getMath())
            );
        }
    }
}
[/code]
그냥 출력합니다-_-; 결과는 이렇게 나올 겁니다.
[code]Hibernate: select score0_.idx as idx1_, score0_.widx as widx1_, score0_.korean as korean1_, score0_.math as math1_ from wondergirls.score score0_
        이름         나이         국어         수학
Hibernate: select wondergirl0_.idx as idx0_0_, wondergirl0_.name as name0_0_, wondergirl0_.age as age0_0_ from wondergirls.wondergirls wondergirl0_ where wondergirl0_.idx=?
        선예         20        100        100
Hibernate: select wondergirl0_.idx as idx0_0_, wondergirl0_.name as name0_0_, wondergirl0_.age as age0_0_ from wondergirls.wondergirls wondergirl0_ where wondergirl0_.idx=?
        선미         20         90         90
Hibernate: select wondergirl0_.idx as idx0_0_, wondergirl0_.name as name0_0_, wondergirl0_.age as age0_0_ from wondergirls.wondergirls wondergirl0_ where wondergirl0_.idx=?
        소희         17         80         80
Hibernate: select wondergirl0_.idx as idx0_0_, wondergirl0_.name as name0_0_, wondergirl0_.age as age0_0_ from wondergirls.wondergirls wondergirl0_ where wondergirl0_.idx=?
        예은         17         70         70
Hibernate: select wondergirl0_.idx as idx0_0_, wondergirl0_.name as name0_0_, wondergirl0_.age as age0_0_ from wondergirls.wondergirls wondergirl0_ where wondergirl0_.idx=?
        유빈         21         60         60
BUILD SUCCESSFUL (total time: 3 seconds)
[/code]
음... 저기서 wondergirls참조하는 것을 가져오려고 하면 다시 쿼리를 날리는 군요-_-; 뭔가 다른 방법이 있는 건가...
Hibernate를 더 공부해봐야겠습니다. 넷빈즈에서 쉽게 Hibernate를 할 수 있네요. 예전에 Hibernate Hello World예제를 따라해보고 셋팅에 겁을 먹은 기억이 있었는데, 쉽게 할 수 있네요.
이제 공부를 해봅시다 ㅠㅠ

 
Posted by 머드초보
,