본문 바로가기

개발

[Apache/MINA] http server 예제

이번 스터디 내용중에 소켓통신에 대한 내용이 있어서 자료조사하다 MINA를 사용해보기로 했다. 

그중에 http server를 구현할 수 있다고 해서 찾아봤는데.. 현재 MINA가 2.0.4버전까지 나왔다. 

근데 2버전때에 예제에는 httpserver가 쏙 빠져있다. 구글링을 해보니 1버전에서 2버전으로 넘어오면서 httpserver가 

asyncweb이라는 하나의 메뉴로 빠져버렸단다. 그게 맞는건지 어쩄든 httpserver를 돌려보기 위해 

예제를 1.7을 다운받아서 예제를 돌려보았다. 


예제 실행방법

1. http://mina.apache.org 에서 소스다운로드 (binary받지말고 source를 받을것)

2. 적당한 위치에서 압축해제

3. eclipse에서 import -> maven -> existing maven project -> Root Direct 를 위에 압축해제한곳으로 설정

4. example/porm.xml 만 체크후 Finish

5. 프로젝트 설정 -> Project facets -> Convert to faceted form... 파란글씨 클릭

6. OK 클릭

7. 그럼 JAVA프로젝트로 변경된걸 확인할 수 있음. 프로젝트 우클릭 -> Run as maven install 

8. 필요한 예제 main 메소드 찾아서 실행.


이제 httpserver 예제를 실행해볼건데 org.apache.mina.example.httpserver 에 가보면 codec 과 stream이라는 패키지가 있다.

stream은 단순히 html을 리턴하는 서버라 생각하면 되고, 파라미터등의 처리를 위해 codec 코드를 본다.

그중에서 Server.java를 실행시켜보자. 그럼 콘솔창에 아래와 같이 뜬다.


Server now listening on port 8090


그 후에 브라우져를 이용해서 접속해보자. 그럼 서버 콘솔창에 아래와 같이 출력되는걸 볼수있다. 
[17:37:08] INFO [org.apache.mina.example.httpserver.codec.ServerHandler] - [/127.0.0.1:9330] RECEIVED: Cookie : ext-ext-gen1069=o%3A; ext-ext-gen1092=o%3A; ext-ext-gen1116=o%3A; ext-navigation-panel=o%3Acollapsed%3Db%253A0%5Ewidth%3Dn%253A200; ext-mainTabPanel=o%3Acollapsed%3Db%253A0%5Eflex%3Dn%253A1; ext-main-viewport-embedded-center=o%3Aflex%3Dn%253A1; ext-main-viewport=o%3Awidth%3Dn%253A1394%5Eheight%3Dn%253A927
Method : GET
Host : localhost:8090
User-Agent : Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/535.19 (KHTML, like Gecko) Chrome/18.0.1025.162 Safari/535.19
Connection : keep-alive
Cache-Control : max-age=0
Accept-Language : ko-KR,ko;q=0.8,en-US;q=0.6,en;q=0.4
If-Modified-Since : 수, 18 4월 2012 17:36:52 KST
Context : 
Accept-Charset : windows-949,utf-8;q=0.7,*;q=0.3
Accept-Encoding : gzip,deflate,sdch
Accept : text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Protocol : HTTP/1.1
URI : GET / HTTP/1.1

[17:37:08] INFO [org.apache.mina.example.httpserver.codec.ServerHandler] - [/127.0.0.1:9330] WRITE: org.apache.mina.example.httpserver.codec.HttpResponseMessage@7881db
[17:37:08] INFO [org.apache.mina.example.httpserver.codec.ServerHandler] - [/127.0.0.1:9330] SENT: org.apache.mina.example.httpserver.codec.HttpResponseMessage@7881db
[17:37:08] INFO [org.apache.mina.example.httpserver.codec.ServerHandler] - [/127.0.0.1:9330] RECEIVED: Cookie : ext-ext-gen1069=o%3A; ext-ext-gen1092=o%3A; ext-ext-gen1116=o%3A; ext-navigation-panel=o%3Acollapsed%3Db%253A0%5Ewidth%3Dn%253A200; ext-mainTabPanel=o%3Acollapsed%3Db%253A0%5Eflex%3Dn%253A1; ext-main-viewport-embedded-center=o%3Aflex%3Dn%253A1; ext-main-viewport=o%3Awidth%3Dn%253A1394%5Eheight%3Dn%253A927
Method : GET
Host : localhost:8090
User-Agent : Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/535.19 (KHTML, like Gecko) Chrome/18.0.1025.162 Safari/535.19
Connection : keep-alive
Accept-Language : ko-KR,ko;q=0.8,en-US;q=0.6,en;q=0.4
If-Modified-Since : 수, 18 4월 2012 17:36:52 KST
Context : favicon.ico
Accept-Charset : windows-949,utf-8;q=0.7,*;q=0.3
Accept-Encoding : gzip,deflate,sdch
Accept : */*
Protocol : HTTP/1.1
URI : GET /favicon.ico HTTP/1.1

[17:37:08] INFO [org.apache.mina.example.httpserver.codec.ServerHandler] - [/127.0.0.1:9330] WRITE: org.apache.mina.example.httpserver.codec.HttpResponseMessage@5ead9d
[17:37:08] INFO [org.apache.mina.example.httpserver.codec.ServerHandler] - [/127.0.0.1:9330] SENT: org.apache.mina.example.httpserver.codec.HttpResponseMessage@5ead9d

접속정보에 대해서 출력된다. 이제 내가 원하는 파라미터를 넘겨서 처리를 하고싶은데.. 소스를 보다보니.. 딱히 감이 안오네.-_-..
그래서 HttpRequestDecoder.java 에 parseRequest 메소드를 수정하여 처리해 보았다. 근데 이렇게 하는게 아닌거 같은 감이 오긴한다..
뭔가 더 편한방법이 있을텐데.. 아시는분 좀 알려주세요..

HttpRequestDecoder.java

 private Map parseRequest(Reader is) {
    	Map map = new HashMap();
        BufferedReader rdr = new BufferedReader(is);

        try {
            // Get request URL.
            String line = rdr.readLine();
            String[] url = line.split(" ");
            
            if (url.length < 3)
                return map;

            map.put("URI", new String[] { line });
            map.put("Method", new String[] { url[0].toUpperCase() });
            map.put("Context", new String[] { url[1].substring(1) });
            map.put("Protocol", new String[] { url[2] });
            
            // Read header
            while ((line = rdr.readLine()) != null && line.length() > 0) {
                String[] tokens = line.split(": ");
                map.put(tokens[0], new String[] { tokens[1] });
            }

            // If method 'POST' then read Content-Length worth of data
            if (url[0].equalsIgnoreCase("POST")) {
                int len = Integer.parseInt(map.get("Content-Length")[0]);
                char[] buf = new char[len];
                if (rdr.read(buf) == len) {
                    line = String.copyValueOf(buf);
                }
            } else if (url[0].equalsIgnoreCase("GET")) {
                int idx = url[1].indexOf('?');
                if (idx != -1) {
                    map.put("Context",
                            new String[] { url[1].substring(1, idx) });
                    line = url[1].substring(idx + 1);
                } else {
                    line = null;
                }
            }

            if (line != null) {
                String[] match = line.split("\\&");
                for (int i = 0; i < match.length; i++) {
                    String[] params = new String[1];
                    String[] tokens = match[i].split("=");
                    switch (tokens.length) {
                    case 0:
                        map.put("@".concat(match[i]), new String[] {});
                        break;
                    case 1:
                        map.put("@".concat(tokens[0]), new String[] {});
                        break;
                    default:
                        String name = "@".concat(tokens[0]);
                        if (map.containsKey(name)) {
                            params = map.get(name);
                            String[] tmp = new String[params.length + 1];
                            for (int j = 0; j < params.length; j++)
                                tmp[j] = params[j];
                            params = null;
                            params = tmp;
                        }
                        
                        params[params.length - 1] = tokens[1].trim();

                        // 넘겨받은 파라미터를 찾아 따로 처리. 
                        // 이렇게 하는게 맞는지는 확실히 모르겠음.                       
                       	if( name.equals("@val")){
                       		String xmlStr = URLDecoder.decode(params[0],"UTF-8");
                       		
                       		DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
                    		try {
                       		DocumentBuilder builder = factory.newDocumentBuilder();
                                InputSource iss = new InputSource( new StringReader( xmlStr ) );
                                Document doc = builder.parse( iss );
								
				NodeList nodelist = doc.getElementsByTagName("test");
				for (int j = 0; j < nodelist.getLength(); j++) {
					NodeList nodeChild = nodelist.item(j).getChildNodes();
				System.out.println(nodeChild.item(0).getTextContent());
				}
								
			} catch (Exception e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
                       	}// end if
                        
                        map.put(name, params);
                    }
                }
            }
        } catch (IOException ex) {
            ex.printStackTrace();
        }
        return map;
    }
위에 소스는 내가 보낸 파라미터(xml데이터)를 String으로 받아서 Document 구조로 변환해주는 작업이다. 그리고 잘 parsing이 되었는지
출력해주는 부분.. 근데 보면 저 map에다가 해당 파라미터를 담아서 넘겨주는데 그걸 어디서 가져다 쓸수있는지는 모르겠다.
client 쪽으로 넘겨주는건가.. 흠.. 알수없넹^^a

이쪽부분을 더 살펴보고 AsyncWeb으로 넘어가야겠다. 근데 이렇게 MINA를 이용하여 http server를 구현하는것이 잇점이 있을까? 경량화?
tomcat이라는 훌륭한 was에 돌려버리면 안되는것일까 하는 생각이 든다.