'잡다구리 샘플'에 해당되는 글 4건

  1. 2011.11.16 제우스 + 스프링 프로젝트 디플로이시 java.lang.IllegalStateException: Unable to locate the default servlet for serving static content 이 발생할때 4
  2. 2011.08.23 jQuery 와 Servlet을 이용한 간단한 Captcha 샘플~ 5
  3. 2011.08.11 CrossDomain Ajax 호출 #2 : jQuery JSONP를 이용한 Daum OpenAPI 활용 2
  4. 2011.08.10 CrossDomain Ajax 호출 #1 : jQuery JSONP ajax 호출 이용. 4

제우스 + 스프링 프로젝트 디플로이시 java.lang.IllegalStateException: Unable to locate the default servlet for serving static content 이 발생할때


개발환경으로 Jetty 를 써가며 개발하다 실제 서버인 JEUS 에 프로젝트를 디플로이 할려고 했는데

java.lang.IllegalStateException: Unable to locate the default servlet for serving static content. Please set the 'defaultServletName' property explicitly.

어쩌고 하는 에러가 났다.




쪽~~~ 찾아보니 에러가 나는 이유는 "/" 경로를 처리하기 위해 등록했던 스프링 설정파일 중의 <mvc:default-servlet-handler/> 요 설정 때문이였다.




원래 <mvc:default-servlet-handler/> 설정할때 <mvc:default-servlet-handler default-servlet-name="디폴트서블릿 이름"/> 요런식으로 WAS 의 디폴트 서블릿 이름을 설정해 줘야 하는데,

tomcat, resin, weblogic, websphere 같은 외국에서 맨든 was 들은 <mvc:default-servlet-handler/> 요렇게 편리하게 설정할수 있도록

스프링 개발자가 주요 was 에 대해 처리를 해줘서, 굳이 디폴트 서블릿 이름을 안적어 줘도 됬었던 것이다.




아무튼 Jeus 에서 에러를 해결하기 위해서는 Jeus 에서 쓰는 디폴트 서블릿 이름인 WorkerServlet 을
<mvc:default-servlet-handler default-servlet-name="WorkerServlet"/>

요래 설정해 주면 해결할 수 있다.





※ 디폴트 서블릿 이름을 알려면 ?
각 was 설치 디렉토리 아래쪽으로 설정 파일들 모아 놓은데 보면 web.xml 파일이 있다. 예를 들어 톰캣 같은 경우는 톰캣설치디렉토리/conf/web.xml 파일이 되겠다.

이 파일을 열어보면
<servlet>
	<servlet-name>default</servlet-name>
	<servlet-class>org.apache.catalina.servlets.DefaultServlet</servlet-class>
	<init-param>
		<param-name>debug</param-name>
		<param-value>0</param-value>
	</init-param>
	<init-param>
		<param-name>listings</param-name>
		<param-value>false</param-value>
	</init-param>
	<load-on-startup>1</load-on-startup>
</servlet>
요렇게 되 있는데 <servlet-name>default</servlet-name> 에서 default 가 디폴트 서블릿 이름이다.





※ was 별 default servlet name
Tomcat : default
Resin : resin-file
Weblogic :  FileServlet
WebSphere : SimpleFileServlet
jetty : default
jboss : default
jeus : WorkerServlet



jQuery 와 Servlet을 이용한 간단한 Captcha 샘플~

Captcha 란 간단히 아래쪽 그림과 같은 기능을 하는것을 말한다.


 

제작해본 샘플은 

서버측에서 이미지를 생성해 주고, 생성된 문자열을 세션에 저장시켜주는 기능을 수행하는 Servlet 하나와

고 서블릿을 사용하는 클라이언트 쪽 html 파일로 구성된다. 

사용한 라이브러리는 랜덤 스트링 생성을 편하게 하기 위해 apache commons lang 의 RandomStringUtils 를 사용했다.


CaptchaGenServlet.java
package servlet;

import java.awt.Color;
import java.awt.Font;
import java.awt.Graphics2D;
import java.awt.GraphicsEnvironment;
import java.awt.RenderingHints;
import java.awt.font.FontRenderContext;
import java.awt.geom.Rectangle2D;
import java.awt.image.BufferedImage;
import java.io.IOException;

import javax.imageio.ImageIO;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.commons.lang.RandomStringUtils;

public class CaptchaGenServlet extends HttpServlet {
	private static final long serialVersionUID = 1L;

	@Override
	public void init() throws ServletException {
		super.init();
		
		// 폰트 등록
		try {
			Font font = Font.createFont(Font.TRUETYPE_FONT,
                                    this.getClass().getResourceAsStream("/servlet/HelveticaMedCd.ttf"));
			GraphicsEnvironment.getLocalGraphicsEnvironment().registerFont(font);
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
	
    public CaptchaGenServlet() {
        super();
    }

	protected void doGet(HttpServletRequest request, HttpServletResponse response) 
                      throws ServletException, IOException {
		generateCaptcha(request, response);
	}

	protected void doPost(HttpServletRequest request, HttpServletResponse response) 
                       throws ServletException, IOException {
		generateCaptcha(request, response);
	}
	
	private void generateCaptcha(HttpServletRequest request, HttpServletResponse response){
		try {
			// 알파벳 숫자섞인 5자리 문자열 생성
			String randomString = RandomStringUtils.randomAlphanumeric(5).toUpperCase();
			
			// 세션에 저장
			request.getSession().setAttribute("CAPTCHA", randomString);
			
			Font font = new Font("Helvetica 67 Medium Condensed", Font.PLAIN, 50);
			FontRenderContext frc = new FontRenderContext(null, true, true);
			Rectangle2D bounds = font.getStringBounds(randomString, frc);
			int w = (int) bounds.getWidth();
			int h = (int) bounds.getHeight();
			
			// 이미지 생성
			BufferedImage image = new BufferedImage(w, h, BufferedImage.TYPE_INT_RGB);
			Graphics2D g = image.createGraphics();
			g.setColor(Color.WHITE);
			g.fillRect(0, 0, w, h);
			g.setColor(new Color(113, 193, 217));
			g.setFont(font);
			g.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, 
                               RenderingHints.VALUE_TEXT_ANTIALIAS_ON);
			g.setRenderingHint(RenderingHints.KEY_FRACTIONALMETRICS, 
                               RenderingHints.VALUE_FRACTIONALMETRICS_ON);
			g.drawString(randomString, (float) bounds.getX(), (float) -bounds.getY());
			g.dispose();

			ImageIO.write(image, "png",  response.getOutputStream());
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
}


이미지 생성시 문자열에 쓸 폰트를 클래스패스에 위치시킨 다음에 30번째 라인처럼 불러와서 폰트를 등록시킬 수 있다.
폰트 등록후 57번째 라인처럼 등록된 폰트를 불러와 쓸 수 있는데 시스템에 등록된 폰트들 이름을 확인하기 위해서는 다음과 같은 코드로 확인하면 된다.
Font[] fontList = GraphicsEnvironment.getLocalGraphicsEnvironment().getAllFonts();
			
for( Font f : fontList){
	System.out.println(f.getName());
}



web.xml 서블릿 설정

<servlet>
	<display-name>CaptchaGenServlet</display-name>
	<servlet-name>CaptchaGenServlet</servlet-name>
	<servlet-class>servlet.CaptchaGenServlet</servlet-class>
</servlet>
<servlet-mapping>
	<servlet-name>CaptchaGenServlet</servlet-name>
	<url-pattern>/captcha</url-pattern>
</servlet-mapping>



Captcha UI
<html>
<head>
	<title>Captcha Test</title>
	
	<script type="text/javascript" src="/jquery-1.6.1.min.js"></script>
	
	<script>
		$(document).ready(function(){
			create();
			
			$("#refreshBtn").click(function(e){
				e.preventDefault();
				create();
			});
			
			$("#confirmBtn").click(function(e){
				e.preventDefault();
				$("#frm").submit();
			});
			
			function create(){
				$("#captcha img").attr("src", "/captcha?"+Math.random());
			}
		});
	</script>
	
	<style>
		#captcha {
			width : 230px;
			height : 70px;
			border: 3px dotted #A3C552;
			text-align: center;
			padding: 5px;
		}
	</style>
</head>

<body>
	Catpcha Test!!!
	<form id="frm" action="result.jsp" method="post">
		<div id="captcha"><img/></div>
		<br/>
		<input type="text" name="captchaInput"/>
		<button id="confirmBtn">확인</button>
		<button id="refreshBtn">리푸레쉬</button>
	</form>
</body>
</html>



쪽바로 입력했는지 체크하기(result.jsp)
SESSION : ${sessionScope['CAPTCHA']}
PARAM : ${param.captchaInput}

통과 : ${ sessionScope['CAPTCHA'] eq param.captchaInput ? 'O' : 'X'}

CaptchaSample.war

CrossDomain Ajax 호출 #2 : jQuery JSONP를 이용한 Daum OpenAPI 활용

CrossDomain Ajax 호출 #1 에서 연구해 보았던 jQuery JSONP 를 활용해 다음 OpenAPI 로 이미지를 검색하는 샘플을 제작해 보았다.

다행스럽게도 다음 오픈 API에서 제공하는 많은 API 들이 대부분 callback이라는 파라메터를 받는다. 그렇기 때문에 JSONP 로 ajax 호출을 통해 깔쌈하게 뭔가를 맨들수 있다.

callback 이라는 파라메터를 받는다!!



찾아봤는데 네이버 오픈 API는 아쉽게도 callback 요런 종류의 파라메터를 받는 API 가 하나도 없는것 같다. 요것은 JSONP 방식이 아닌 Proxy Servlet 방식을 이용해서 데이터를 받아야만 한다. 뭐 이것은 차후에 한번 해 봐야지~

먼저 샘플을 맨들기 전에 다음 OpenAPI 사이트(http://dna.daum.net/DNALatte/openapi/about)에 가서 Key를 발급받아야 한다. 샘플로 검색API 에 있는 이미지 검색 API를 활용할 것이기 때문에 검색 API용 키를 발급 받는다.

localhost:8080 에서 테스트를 할것이기 때문에 사용URL 란에는 http://localhost:8080 을 입력하고 확인 버튼을 클릭하자! 고러면 키가 발급된다.

키 발급화면




키를 발급 받았다면 샘플 제작 고고씽~

이미지 API 를 호출하는 jQuery 코드는 요렇게~
$.ajax({
	url : "http://apis.daum.net/search/image",
	dataType : "jsonp",
	type : "post",
	jsonp : "callback",
	data : {
		apikey : "발급받은 Key",        // API KEY
		q : "한예슬",                   // 검색어
		result : "10",                 // 한페이지에 출력될 결과수
		pageno : $("#pageno").val(),   // 페이지번호
		output : "json"                // JSONP 형식으로 호출하기 때문에 결과값 포맷은 json
	},
	success : function(r){
		//검색결과 처리
	}
});


검색된 결과는 json 형태로 리턴된다. json으로 보내달라고 요청했으니깐~
console에다 찍어보니 데이터 형태가 죠렇게 되있다. 검색결과에 대한 자세한 설명은 http://dna.daum.net/apis/search/image 요기에서 참고하면 된다.

검색결과 JSON 오브젝트 데이터구조


검색결과는 대충보니 channel 안에 item 에 배열로 들어가 있다~ 뭐 하여튼 저 검색결과를 활용해서 쫌 쓸만하게 제작을 하면 되는 것이다!

샘플로 맨들어본 api.html 의 전체소스
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html lang="ko">
<head>
	<meta http-equiv="Content-Type" content="text/html;charset=UTF-8">
	<title>Daum OpenAPI with jQuery</title>
	
	<script type="text/javascript" src="/resource/js/jquery-1.6.2.min.js"></script>
	
	<script type="text/javascript">
		$(document).ready(function(){
			// 이미지 검색 할래염 버튼
			$("#searchBtn").click(function(){
				// 기존 검색결과랑 조건 지우기
				$("#imgList").empty();
				$("#pageInfo").val("1");
				
				searchImg();
			});
			
			// 더보기버튼
			$("#moreBtn").click(function(){
				$("#pageno").val(parseInt($("#pageno").val())+1);
				
				searchImg();
			});
			
			var searchImg = function(){
				$.ajax({
					url : "http://apis.daum.net/search/image",
					dataType : "jsonp",
					type : "post",
					jsonp : "callback",
					data : {
						apikey : "발급받은키",               // API KEY
						q : $("#keyword").val(),         // 검색어
						result : "10",                   // 한페이지에 출력될 결과수
						pageno : $("#pageno").val(),     // 페이지번호
						output : "json"                  // JSONP 형식으로 호출하기 때문에 결과값 포맷은 json
					},
					success : function(r){
						r = r.channel;
						$("#pageInfo").text("검색된 이미지는 " + r.totalCount +"개 에요~")
						
						$.each(r.item, function(idx, data){
							var img = $("").attr({
								src : data.thumbnail,
								title : data.title
							}).click(function(){
								window.open(data.image);
							}).appendTo("#imgList");
						});
					}
				});
			};
		});
	</script>
	
	<style type="text/css">
		#result {width:800px;}
		#imgList {width: 100%;border:2px solid #ccc; padding:5px}
		#imgList img{border:1px solid #cccccc;padding:2px;margin:5px;cursor:pointer}
		#moreBtn {width:100%;height:60px;}
	</style>
</head>
<body>
	<input type="hidden" id="pageno" value="1"/>
	<input type="text" id="keyword" />
	<button id="searchBtn">이미지 검색 할래염</button>
	
	<div id="result">
		<span id="pageInfo"></span>
		<div id="imgList">여기에 검색된 이미지가 나타날꺼임</div>
		<button id="moreBtn">더보기</button>
	</div>
</body>
</html>

[샘플소스]DaumAPI.war


실행결과 Demo 원하는 검색어를 입력후 "이미지 검색 할래염" 버튼을 클릭해 보셈~
여기에 검색된 이미지가 나타날꺼임

CrossDomain Ajax 호출 #1 : jQuery JSONP ajax 호출 이용.

Ajax 로 다른 도메인에 있는 url 을 호출해 데이터나 HTML을 가져오는건 보안상 안된다고 한다. 되면 참 좋을텐데;;

뭐 아무튼 이걸 해결하기 위한 방법으로 JSONP 라는 형식으로 호출하던가 아니면 Proxy Servlet 을 하나 맹글어 그 서블릿을 거쳐 HttpClient 로 가져오는 방법, 훌래쉬를 이용하는 방법이 있다고 한다.!

그중에서 이번엔 JSONP 형식으로 호출하는 방법을 연구해 보겠다.

JSONP 형식이란 뭐 별건 아닌것 같고 기존에 서버에서 리턴해 주던

{"key1" : "value1",  "key2" : "value2"} 요런 json 문자열을

callback({"key1" : "value1",  "key2" : "value2"}); 요런식으로 임의의 함수를 호출해 주는 형식의 문자열로 리턴해 주는것을 말하는것 같다.

즉, jsonp 로 ajax 호출을 하기 위해선 아무 url 이나 안되고  callback({"key1" : "value1",  "key2" : "value2"});  요런식으로 함수안에 json 문자열이 들어간 형식으로 서버에서 리턴을 해줘야 가능하다.

각설하고 jQuery를 활용해 맹글어 보자.



jQuery에서 jsonp 호출하는 방법#1
$.getJSON("http://127.0.0.1:8080/server/data.jsp?callback=?", function(d){
	//d.key;
});

jQuery에서 jsonp 호출하는 방법#2
$.ajax({
	url : "http://127.0.0.1:8080/server/data.jsp",
	dataType : "jsonp",
	jsonp : "callback",
	success : function(d){
		// d.key;
	}
});

방법#1 이나 방법#2는 작동하는게 동일하다. 취향에 맞게 쓰임새에 맞게 쓰면된다. 개인적으로 방법#2 를 여러가지 원하는 ajax 호출 옵션을 줄수 있어서 더 좋아한다.

살짝 설명해 보면 방법#2 에서 쓴 ajax 옵션중 jsonp는 데이터를 넘겨줄 서버에서 받는 callback 함수명 파라메터이다. 서버에 따라 원하는 callback 함수명을 적어줘야 한다. 방법#1을 보면 대충 이해할 수 있다.

jsonp 옵션에다 적어준 함수명은 ajax 호출 url 뒤에다 방법#1에서 처럼 파라메터 형식으로 자동 추가된다.



전체소스코드 client.html
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
	<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
	<title>Jsonp test with jquery</title>
	
	<script type="text/javascript" src="/resource/js/jquery-1.6.2.min.js"></script>
	<script>
		$(document).ready(function(){
			$("#testBtn").click(function(){
				$.getJSON("http://127.0.0.1:8080/server/data.jsp?callback=?", function(d){
					$.each(d, function(k, v){
						$("#getjson").append("<div>" + k + " : " + v + "</div>");
					});
					$("#getjson").show();
				});
							
				$.ajax({
					url : "http://127.0.0.1:8080/server/data.jsp",
					dataType : "jsonp",
					jsonp : "callback",
					success : function(d){
						$.each(d, function(k, v){
							$("#ajax").append("<div>" + k + " : " + v + "</div>");
						});
						$("#ajax").show();
					}
				});
			});
		});
	</script>
	
	<style>
		div{margin-bottom:10px;padding:2px;}
		#getjson{border:1px solid red;display:none;}
		#ajax{border:1px solid blue;display:none;}
	</style>
</head>

<body>
	<button id="testBtn">테스트!</button>
	<div id="getjson"></div>
	<div id="ajax"></div>
</body>
</html>



다음으로 간단한 테스트를 위해 초간단 심플 데이터 제공용 jsp 파일을 하나 맹글어 보자. JSON 변환용 라이브러리로 Jackson JSON Processor를 사용했다.
<%@ page language="java" contentType="text/javascript; charset=UTF-8" pageEncoding="UTF-8"%>

<%@ page import="java.io.StringWriter"%>
<%@ page import="java.util.HashMap"%>
<%@ page import="java.util.Map"%>

<%@ page import="org.codehaus.jackson.map.ObjectMapper"%>

<%
	Map<String, String> dummyData = new HashMap<String, String>();
	dummyData.put("value1", "값1");
	dummyData.put("value2", "값2");
	dummyData.put("value3", "값3");
	dummyData.put("value4", "값4");
	
	StringWriter sw = new StringWriter();
	
	// Jackson JSON Mapper 를 사용해서 Map 을 JSON 문자열로 변환
	ObjectMapper mapper = new ObjectMapper();
	mapper.writeValue(sw, dummyData);
	
	request.setAttribute("sw", sw);
%>

<%-- ajax 에서 넘겨준 callback 함수 파라메터 가져오기 --%>
${param.callback}(${sw});
ajax로 호출한 url 경로에 맞게금 /server/data.jsp 로 저장하고 http://localhost:8080/client.html 로 접속해보자. 왜냐하면 localhost 랑 127.0.0.1 은 도메인이 다르니깐.  

음 잘 작동된다~~ 초간단 심플 예제이기 때문에 간단하게 JSP로 구현했는데 다음번에 쫌더 실제적으로 프로젝트에 써먹을수 있도록 Spring으로 예제를 하나 맹글어 봐야긋다~

[샘플소스]CrossDomain1.war
prev 1 next