기록용~!

아오.....안드로이드에다가 웹서버를 올려서 뭔가를 하려는 걸 구현하려고 하는데, 안드로이드에서는 com.sun.net.httpserver.HttpServer 이 클래스가 없네요ㅠㅠ 필요 없을 것 같아서 뺀 듯ㅠㅠ 전에 찾았던 소스도 다시 못찾겠고.....선호한테 물어봐야겠네....

암튼 초간단 서버 구축 클래스가 있었네요.

import java.io.IOException;
import java.net.InetSocketAddress;
import java.util.concurrent.Executors;

import com.sun.net.httpserver.HttpServer;


public class Test {

	public static void main(String[] args) throws IOException {
		InetSocketAddress addr = new InetSocketAddress(9000);
		HttpServer server = HttpServer.create(addr, 0);
		
		server.createContext("/", new MyHandler());
		server.setExecutor(Executors.newCachedThreadPool());
		server.start();
	}
}

해당포트로 HttpServer.create로 서버 생성하고, 핸들러클래스를 파라메터로 넣으면 저기 핸들러에서 다 처리를 하는 듯하네요.

MyHandler.java

import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;
import java.io.OutputStream;
import java.net.URI;

import com.sun.net.httpserver.Headers;
import com.sun.net.httpserver.HttpExchange;
import com.sun.net.httpserver.HttpHandler;


public class MyHandler implements HttpHandler {

	private String root = "D:/root/";
	
	@Override
	public void handle(HttpExchange exchange) throws IOException {
		String requestMethod = exchange.getRequestMethod();
		if (requestMethod.equalsIgnoreCase("GET")){
			Headers responseHeaders = exchange.getResponseHeaders();
			responseHeaders.set("Content-Type", "text/html");
			URI uri = exchange.getRequestURI();
			System.out.println(uri.getPath());
			OutputStream responseBody = exchange.getResponseBody();
			BufferedReader br = new BufferedReader(new FileReader(root + uri.getPath()));
			exchange.sendResponseHeaders(200, 0);
			int b = 0;
			while((b = br.read()) != -1){
				responseBody.write(b);
			}
			responseBody.close();
		}
	}
}
get 요청인 경우 해당 path에서 파일을 찾아서 그냥 뿌려주는 게 다임 ㅇㅇ
지금셋팅은 http://localhost:9000/index.html을 요청하면  d:/root/index.html 파일을 읽어서 뿌려줌 ㅇㅇ

ps. 생각해보니 파일을 주고받으려고 찾고 있었는데, ftp서버를 찾았어야 했는데......젠장......ㅠㅠ
 
Posted by 머드초보
,
 
앱엔진 짱이네요-_-
속도만 빨랐다면 호스팅을 여기로 옮겨도 뭐 문제가 없을 듯. 시스템적으로는 구글이 다 관리해주고, 개발환경도 매우 편하니...(하지만, DB 접근이 방식이 틀려서 힘든면도 있지만ㅠ)

pc랑 android랑 뭔가 싱크맺는 뭐 그런거 만드려다보니 여기까지 와버렸네요-_- 이걸 통해서 하면 좋을 것 같아서^^

암튼, Channel API를 제공하는데, 원하는 채널을 생성해서 그곳 페이지로 접속한 사용자들끼리 메세지를 주고 받을 수 있게 됩니다. 

앱엔진 채널부분 문서입니다.

보면 JavaAPI랑 JavascriptAPI 두개 있는데, 클래스도 몇 개 없습니다.
Java에서는 ChannelService를 가져와서 createChannel로 채널을 만든 뒤에, sendMessage함수로 메세지만 보내면 됩니다.
Javascript에서는 goog.appengine.Channel이라는 클래스가 존재하는데, 이걸로 오픈하고 메세지 받으면 됩니다.

일단..... 나중에 쓰려고 만든거다보니 Spring3.0과 jQuery가 들어갔네요-_-
http://mudchobo.tistory.com/470 이거 참조해서 환경 구축을 하면 됩니다~^^

ChannelController.java

package com.mudchobo.apps.exchangepp.controller;

import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;

import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;

import com.google.appengine.api.channel.ChannelMessage;
import com.google.appengine.api.channel.ChannelService;
import com.google.appengine.api.channel.ChannelServiceFactory;

@Controller
@RequestMapping("/channeltest")
public class ChannelController {
	
	private String channelName = "test";
	
	@RequestMapping(value="/test", method=RequestMethod.GET)
	public String test(Model model){
		ChannelService channelService = ChannelServiceFactory.getChannelService();
		String token = channelService.createChannel(channelName);
		model.addAttribute("token", token);
		return "test";
	}
	
	@RequestMapping(value="/open", method=RequestMethod.POST)
	@ResponseBody
	public String openChat(){
		ChannelService channelService = ChannelServiceFactory.getChannelService();
		channelService.sendMessage(new ChannelMessage(channelName, "open"));
		return "";
	}
	
	@RequestMapping(value="/send", method=RequestMethod.POST)
	@ResponseBody
	public String sendChat(@RequestParam("msg") String msg){
		System.out.println("message = " + msg);
		try {
			ChannelService channelService = ChannelServiceFactory.getChannelService();
			channelService.sendMessage(new ChannelMessage(channelName, URLEncoder.encode(msg, "UTF-8")));
		} catch (UnsupportedEncodingException e) {
			e.printStackTrace();
		}
		return "";
	}
}
최초 접속페이지는 /test 입니다.
여기서 채널을 생성해줍니다. 이 생성해서 나온 토큰을 client로 전달합니다. 그러면 그 client에서 goog.appengine.Channel("토큰값");을 통해 Channel을 생성합니다. 그러면 커넥션을 맺고 있게 되는겁니다.

클라이언트부분
test.jsp

<%@ page language="java" contentType="text/html; charset=utf-8" pageEncoding="UTF-8"%>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<%@ page isELIgnored="false" %>
<!doctype html>
<html>
	<head>
		<script src="/_ah/channel/jsapi"></script>
		<script src="/js/lib/jquery-1.4.4.min.js" type="text/javascript"></script>
		<script type="text/javascript">
			var sendMessage = function(path, msg){
				var xhr = new XMLHttpRequest();
				xhr.open("POST", "/apps/channeltest/" + path + "?msg=" + encodeURIComponent(msg), true);
				xhr.send();
			}
			
			var onOpen = function(){
				sendMessage("open");
			};
			var onMessage = function(m){
				var msg = decodeURIComponent(m.data.split("+").join(" "));
				$("#message_box ul").append("<li>" + msg + "</li>");
				$("#message_box").scrollTop(999999999);
			};
			var onError = function(){
				alert("error");
			};
			var onClose = function(){
				alert("close");
			};
			
			$(document).ready(function(){
				var channel = new goog.appengine.Channel("${token}");
				var socket = channel.open();
				socket.onopen = onOpen;
				socket.onmessage = onMessage;
				socket.onerror = onError;
				socket.onclose = onClose;
				
				$("#send_btn").click(function(){
					sendMessage("send", $("#message").val());
					$("#message").val("");
				});
				$("#message").keyup(function(e){
					if (e.keyCode == 13){
						sendMessage("send", $("#message").val());
						$("#message").val("");
					}
				});
			});
		</script>
	</head>
	<body>
		<div id="message_box" style="width:500px;height:300px;overflow:scroll;font-size:12px">
			<ul></ul>
		</div>
		<input type="text" id="message" name="message" />
		<input type="button" value="보내기" id="send_btn" name="send_btn" /><br />
	</body>
</html>
일단 Channel클래스를 쓰려면 <script src="/_ah/channel/jsapi"></script>를 include해야해요. 그리고, new goog.appengine.Channel생성부분을 유심히 보면 함수로 onopen, onmessage, onerror, onclose를 등록할 수 있어요. 메세지를 받으면 onmessage가 호출이 되니 여기서 처리하면 됨 ㅇㅇ 보내는 것은 아까 java에서 만들어 놓은 url을 호출하면 됨. 끗~

예제주소는 여기

소스파일...귀찮아서 그냥....통째로.....-_-


ps. 한글이 깨지길래 보낼 때 인코딩하고, 다시 받을 때 url인코딩해서 보내고 받은 걸 푸는 방식으로 했더니 되네요. 

 
Posted by 머드초보
,
 
이런건 기록해줘야....

그냥 인터넷뱅킹으로 이체하다가 이놈의 보안카드 맨날 들고 다닐 수 없다 라는 생각에 보안카드 앱을 만드려고 했는데, 좋은 거 많이 있네요.....역시 내가 생각할 수 있는 건 다있어...ㅠㅠ 

보안카드앱은 "SaveMoney 보안카드 신용카드" 앱이 짱인 것 같네요. 제가 원하는대로 만들었네요~ 대신 광고없는 버전은 유료 ㄷㄷ 하지만, 광고 그까이꺼.....

암튼 얘기가 샜네요...-_-

안드로이드에서는 로컬 데이터베이스를 사용할 수 있습니다. 그것도 매우 손쉽게~!
SQLiteOpenHelper라는 매우 친절한 클래스가 있어서 더욱 쉽게 할 수 있어요~

SQLiteOpenHelper 클래스

package com.mudchobo.android.secretcard.utils;

import android.content.Context;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
import android.database.sqlite.SQLiteDatabase.CursorFactory;

public class CardDBHelper extends SQLiteOpenHelper
{
	public CardDBHelper(Context context, String name, CursorFactory factory,
			int version)
	{
		super(context, "card.db", null, 1);
	}
	
	@Override
	public void onCreate(SQLiteDatabase db)
	{
		db.execSQL("create table card (_id INTEGER PRIMARY KEY AUTOINCREMENT, name TEXT);");
		db.execSQL("insert into card (_id, name) values (null, '신한카드');");
		db.execSQL("insert into card (_id, name) values (null, 'KB카드');");
		db.execSQL("insert into card (_id, name) values (null, '씨티카드');");
	}

	@Override
	public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) 
	{
		db.execSQL("drop table if exists card");
	}
}
보면 onCreate와 onUpgrade함수를 오버라이드를 합니다.
이거 보면 create는 현재 생성하려는 파일이 없을 때 최초 한번만 실행하게 됩니다. 테이블을 생성하고 기본값을 넣은 최초의 작업을 여기서 하면 됩니다.
onUpgrade는 db테이블 구조가 바뀌거나 칼럼이 추가 되었을 때 호출하게 됩니다. 호출하는 방법은 생성자에서 마지막 인자로 입력한 version을 더 높은 숫자를 입력하게 되면  upgrade가 호출이 되어 실행하게 됩니다. 여기서 마이그레이션작업을 해주시면 됩니다^^
이렇게 하면 나중에 업데이트 되었을 때 사용자폰에 있는 파일을 업그레이드할 수 있고 그런 식으로 만들어놨네요~



실제 사용할 때
package com.mudchobo.android.secretcard;

import java.util.ArrayList;
import java.util.List;

import android.app.ListActivity;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.os.Bundle;
import android.widget.ArrayAdapter;

import com.mudchobo.android.secretcard.utils.CardDBHelper;

public class SecretCardActivity extends ListActivity{
	private CardDBHelper helper;
	
    @Override
    public void onCreate(Bundle savedInstanceState){
        super.onCreate(savedInstanceState);
        
        helper = new CardDBHelper(this, null, null, 0);
        
        List<String> cards = getCards();
        ArrayAdapter<String> adapter = new ArrayAdapter<String>(this, android.R.layout.simple_list_item_1, cards);
        setListAdapter(adapter);
    }
	
	public List<String> getCards(){
		SQLiteDatabase db = helper.getReadableDatabase();
		Cursor cursor = db.rawQuery("select _id, name from card", null);
		List<String> list = new ArrayList<String>();
		while (cursor.moveToNext()){
			list.add(cursor.getString(1));
		}
		return list;
	}
}

helper클래스 생성한다음에, getReadableDatabase() 로 db를 가져와서 쿼리를 날리던 뭐하던 거기에 있는 함수를 쓰면 됩니다~^^ insert같은 건 getWritableDatabase()로 db를 가져와야해요~!

그리고, 안드로이드는 현재 데이터베이스에 뭐가 저장이 되어있는지 확인하기가 매우 힘듭니다. 확인하려면 adb로 콘솔로 들어가서 직접 타이핑해가면서 때려봐야합니다.
손쉽게 확인하려면 루팅하고 root explorer깔면 됩니다(응?)
그러면 이렇게 나와요~ 아핫~!^^

게다가 SQLite Editor라는 앱을 깔면 이 파일을 직접 수정할 수 있답니다. 근데....3딸라짜리 유료앱이네요ㅠㅠ 근데, 뭐 편집 같은 건 코드에서 하는 게 개발자들은 더 편할테니 뭐 굳이 구입하지 않으셔도 아핫~^^




 
Posted by 머드초보
,
 
페이스북 개편되면서 방식이 바뀌었어요 ㅇㅇ

일단 그냥 쓰는건 안되구요. 애플리케이션을 거쳐서 쓰면 써지더라구요. 왜 그냥 삽입하는 방법은 뭔가 보안적인 문제가 있어서 그럴 것 같은데... 애플리케이션을 사용해서 하게 되면 이상한 것을 포스팅 시키는 앱은 차단시켜버리면 되니깐요.

일단 현재 페이스북에서는 유튜브 동영상은 링크만으로도 자동으로 삽입이 되어서 클릭하면 페이스북 페이지에서 바로 동영상 감상할 수 있는 구조가 되어있죠.
일부 어떤 앱들은 포스팅할 때 플래시를 포스팅을 하더라구요. 그래서 어떻게 하는거지 찾아보다가 찾게 되었는데, 그냥 담벼락에 포스팅할 때 파라메터에 source를 추가하고 그 source에서 swf경로를 넣으면 되더라구요.

graph api중 하나인 담벼락에 글쓰기 /me/feed를 호출하는데, source에 swf경로만 넣으면 끝임 ㅇㅇ

간단한 예제

<!DOCTYPE html>
<html>
	<head>
		<style type="text/css">
			.login{}
			.write{display:none}
		</style>
		<script type="text/javascript" src="/js/jquery-1.4.4.min.js"></script>
		<script type="text/javascript">
			$(document).ready(function(){
			});
		</script>
	</head>
	<body>
		<div id="fb-root"></div>
		<script src="http://connect.facebook.net/en_US/all.js"></script>
		<script>
		  FB.init({
		    appId  : '126292714090241',
		    status : true, // check login status
		    cookie : true, // enable cookies to allow the server to access the session
		    xfbml  : true  // parse XFBML
		  });
		  
		  FB.login(function(response){
		  	if (response.session){
				$(".login").hide();
				$(".write").show();
				
				$("#btnMessage").click(function(){
					$(".write").hide();
					$(".message").html("글쓰는 중!!!");
					var message = "아이유의 I Believe~!";
					var picture = "http://profile.ak.fbcdn.net/hprofile-ak-snc4/hs625.ash1/27538_121461621211921_7447_n.jpg";
					var link = "http://todayhumor.co.kr/board/search_view.php?table=humorbest&no=321269";
					var source = "https://t1.daumcdn.net/cfile/tistory/131B54424D1DEEB51C";
					var name = "";
					var description = "";
					var type = "swf";
					FB.api("/me/feed", "post", {message:message, picture:picture, source:source, link: link, name: name, type: type }, function(response){
						$(".message").html("");
						$(".write").show();
						if (!response || response.error){
							alert("Error occured: " + response.error.message);
						} else {
							alert("Post ID: " + response + ", 글쓰기 완료~!");
						}
					});
				});
			} else {
				alert("로그인 취소!");
			}
		  }, {perms:"read_stream, publish_stream, offline_access"});
		</script>
		<div class="login">
		</div>
		<div class="write">
			<input type="button" id="btnMessage" value="글쓰기"/>
		</div>
		<div class="message">
		</div>
	</body>
</html>
로그인하고 글쓰기 하면 아이유 동영상이 하나 포스팅될겁니다-_-
예제를 보면 그냥 로그인 후에 /me/feed에 해당 파라메터로 글을 쓰는 게 다입니다.
참고하세요~^^

PS. 출처는.....오늘의유머에요~ㅠㅠ


 
Posted by 머드초보
,
 
새해 첫 글이네요~ 후....너무 게을러졌어요ㅠㅠ 올해에는 조금 부지런하게 살아야겠습니다.ㅠㅠ 

책 하나 소개하려구요~ 스프링 시큐리티3! 스프링을 쓰시는 분들이라면 다 들어봤을 스프링시큐리티! 하지만 어렵죠ㅠㅠ 

스프링시큐리티3스프링프레임워크기반표준보안솔루션
카테고리 컴퓨터/IT > 네트워크/보안 > 보안/인증/해킹
지은이 피터 뮬라리엔 (위키북스, 2010년)
상세보기



스프링을 사용하고, 인증 및 로그인 관련된 부분은 직접 구현해서 하는 것보단 이 스프링 시큐리티를 사용해서 하는 편이 훨씬 낫습니다. 훨씬 편하게 만들어주거든요~

근데, 가장 어려운 부분은 이 스프링 시큐리티를 대체 어떻게 쓰는거야? 라는 겁니다. 저도 그것이 너무 어려워서 감히 접근을 못했던 부분이기도 하구요.

그것에 대한 해결방안으로 스프링시큐리티3 책이 나왔어요~ 일단 처음에 접근하기 힘든 것은 어려운 용어와 보안에 대한 이해 부족이 가장 큰게 아닌가 싶네요. 

이 책에서는 그냥 처음부터 차근차근 간단한 예제부터 시작해서 스프링 시큐리티를 기존 프로젝트에 적용시켜나가는 형태로 진행됩니다. 로그인과 인증 저장소와의 연동 및 remember me 같은 기능을 매우 손쉽게 config 옵션만 써서 구현할 수 있어요~ 물론 디폴트옵션은 그렇게 쉽게 되고, 어느정도의 커스터마이징도 쉽게 할 수 있도록 책에 잘 나와있구요~ 

적용하고 싶은데 망설이고 계신 분들은 바로 읽으시면 답이 나올겁니다 아핫^^


PS. 책 감사해요~^^
 
Posted by 머드초보
,