AJAX Login with Spring Security

Posted at 2013. 12. 12. 02:52 | Posted in Framework/Spring Security
반응형

Introduction


스프링 시큐리티를 이용하여 로그인을 처리할 때에 AJAX 방식으로 로그인 하는 방법이다.


크게 2가지로 볼 수 있겠다. ㅎㅎ




Using Handler


기본적인 10단계의 필터 체인 중에 UsernamePasswordAuthencationFilter 단계의 "authentication-success-handler-ref"와 "authentication-success-handler-ref" 를 이용하는 방법이다.


	<security:http auto-config="true" disable-url-rewriting="true" use-expressions="true">
		<security:intercept-url pattern="/login.*" access="permitAll" />		
		<security:intercept-url pattern="/**" access="hasRole('ROLE_USER')" />
		<security:form-login login-page="/login.html"
			username-parameter="id"
			password-parameter="password"
			login-processing-url="/login.ajax" default-target-url="/index.html"
			authentication-success-handler-ref="loginSuccessHandler"
			authentication-failure-handler-ref="loginFailureHandler" />
		<security:logout invalidate-session="true"
			logout-url="/logout"
			logout-success-url="/" />
	</security:http>
	
	<!-- 로그인 성공시 핸들러 -->
	<bean id="loginSuccessHandler" class="ajax.login.security.LoginSuccessHandler" />
	<!-- 로그인 실패시 핸들러 -->
	<bean id="loginFailureHandler" class="ajax.login.security.LoginFailureHandler" />


클래스 구조를 보면 아래와 같다.




아래와 같은 필터 체인의 순서롤 작동하게 된다.




구현 방법은 각각의 핸들러에서 응답을 JSON[각주:1]이나 원하는 포멧으로 만들어서 출력하면 된다.


public class LoginSuccessHandler implements AuthenticationSuccessHandler {
	@Override
	public void onAuthenticationSuccess(HttpServletRequest request,
		HttpServletResponse response, Authentication authentication) throws IOException,
		ServletException {

		ObjectMapper om = new ObjectMapper();

		Map<String, Object> map = new HashMap<String, Object>();
		map.put("success", true);
		map.put("returnUrl", getReturnUrl(request, response));

		// {"success" : true, "returnUrl" : "..."}
		String jsonString = om.writeValueAsString(map);

		OutputStream out = response.getOutputStream();
		out.write(jsonString.getBytes());
	}

	/**
	 * 로그인 하기 전의 요청했던 URL을 알아낸다.
	 * 
	 * @param request
	 * @param response
	 * @return
	 */
	private String getReturnUrl(HttpServletRequest request, HttpServletResponse response) {
		RequestCache requestCache = new HttpSessionRequestCache();
		SavedRequest savedRequest = requestCache.getRequest(request, response);
		if (savedRequest == null) {
			return request.getSession().getServletContext().getContextPath();
		}
		return savedRequest.getRedirectUrl();
	}

}

public class LoginFailureHandler implements AuthenticationFailureHandler {

	@Override
	public void onAuthenticationFailure(HttpServletRequest request,
		HttpServletResponse response, AuthenticationException exception)
		throws IOException, ServletException {

		ObjectMapper om = new ObjectMapper();

		Map<String, Object> map = new HashMap<String, Object>();
		map.put("success", false);
		map.put("message", exception.getMessage());

		// {"success" : false, "message" : "..."}
		String jsonString = om.writeValueAsString(map);

		OutputStream out = response.getOutputStream();
		out.write(jsonString.getBytes());

	}

}


ajax.login.1.war


ajax.login.1.zip





Using @MVC


다른 방법으로 필터체인을 사용하지 않고 컨트롤러에서 직접 인증을 처리하는 방법이 있다.


개인적으로 이 두번째 방법을 선호한다. 왜냐하면 요청을 "application/json" 으로 유동적으로 한다던가 입출력관련 AOP 등등을 내 맘대로 사용할 수 있다. 그냥 컨트롤러니까~


@Controller
public class LoginController {

	@RequestMapping(value = "/login.html", method = RequestMethod.GET)
	public void loginPage(ModelAndView mav) {

	}

	@Autowired
	AuthenticationManager authenticationManager;
	@Autowired
	SecurityContextRepository repository;

	@RequestMapping(value = "/login.ajax", method = RequestMethod.POST)
	@ResponseBody
	public ModelMap login(HttpServletRequest request, HttpServletResponse response,
		@RequestParam(value = "id") String username,
		@RequestParam(value = "password") String password) {

		ModelMap map = new ModelMap();

		UsernamePasswordAuthenticationToken token = new UsernamePasswordAuthenticationToken(
			username, password);

		try {
			// 로그인
			Authentication auth = authenticationManager.authenticate(token);
			SecurityContextHolder.getContext().setAuthentication(auth);
			repository.saveContext(SecurityContextHolder.getContext(), request, response);

			map.put("success", true);
			map.put("returnUrl", getReturnUrl(request, response));
		} catch (BadCredentialsException e) {
			map.put("success", false);
			map.put("message", e.getMessage());
		}

		return map;
	}

	/**
	 * 로그인 하기 전의 요청했던 URL을 알아낸다.
	 * 
	 * @param request
	 * @param response
	 * @return
	 */
	private String getReturnUrl(HttpServletRequest request, HttpServletResponse response) {
		RequestCache requestCache = new HttpSessionRequestCache();
		SavedRequest savedRequest = requestCache.getRequest(request, response);
		if (savedRequest == null) {
			return request.getSession().getServletContext().getContextPath();
		}
		return savedRequest.getRedirectUrl();
	}

}


스프링 시큐리티 설정 하는 부분에서 그냥 <security:http> 를 써도 되지만 사용하지 않는 UsernamePasswordAuthenticationFilter 같은 필터 체인을 사용하지 않기 위해 수동으로 구성해 보았다. 꽤 어렵... -_-;; (자세한 내용은 context-security.xml 참조!)


ajax.login.2.war


ajax.login.2.zip





그 밖에도 요청 헤더를 까서 요청이 AJAX 인지 아닌지를 판단해서 다른 처리를 하는 것도 있고, 구현할 수 있는 방법은 무궁무진하다.


  1. JSON(제이슨, JavaScript Object Notation)은, 인터넷에서 자료를 주고받을 때 그 자료를 표현하는 방법이다. 자료의 종류에 큰 제한은 없으며, 특히 컴퓨터 프로그램의 변수값을 표현하는 데 적합하다. 그 형식은 자바스크립트의 구문 형식을 따르지만, 프로그래밍 언어나 플랫폼에 독립적이므로 C, C++, C#, 자바, 자바스크립트, 펄, 파이썬 등 많은 언어에서 이용할 수 있다. [본문으로]
반응형

'Framework > Spring Security' 카테고리의 다른 글

Spring Security Session Destroy  (4) 2013.10.27
MySql Password Encoder  (0) 2013.09.21
//

JSTL Custom Tag using Spring Beans

Posted at 2013. 12. 1. 17:37 | Posted in Java+/Example
반응형

Introduction


JSTL[각주:1] 태그를 만들어 사용할 때에 스프링 빈을 가져다 쓰는 방법이다.




Using Spring Beans


너무나도 간단하다. 


일반적으로 TagSupport 클래스를 상속 받아서 구현 하면 되는데, 스프링 빈을 사용하고 싶으면 RequestContextAwareTag 클래스를 상속 받아서 구현하면 된다.



사용법은 아래와 같다.


import org.springframework.web.context.WebApplicationContext;
import org.springframework.web.servlet.tags.RequestContextAwareTag;

public class XXXTag extends RequestContextAwareTag {

	@Override
	protected int doStartTagInternal() throws Exception {
		// WebApplicationContext 를 얻는다.
		WebApplicationContext ctx = getRequestContext().getWebApplicationContext();
		// 빈을 가져와서 처리
		return SKIP_BODY;
	}

}




Example Source


WAR 파일 (소스 포함)


webapp.war


프로젝트 파일 (메이븐 구조)


webapp.zip




  1. 자바 서버 페이지 표준 태그 라이브러리(JavaServer Pages Standard Tag Library, 약칭 JSTL)은 Java EE 기반의 웹 애플리케이션 개발 플랫폼을 위한 컴포넌트 모음이다. [본문으로]
반응형

'Java+ > Example' 카테고리의 다른 글

LOGBack Configurator with JMX  (0) 2014.07.20
현지어로 언어명 보여주기  (0) 2014.02.09
2014년 도로명 주소 사용에 따른 우편번호 준비  (2) 2013.12.22
Spring Message Source from Database  (1) 2013.03.03
Infinite Routing DataSource  (1) 2013.01.13
Mybatis Type Handler  (1) 2012.09.30
Using AUTO_INCREMENT keys  (0) 2011.03.01
//

Spring Security Session Destroy

Posted at 2013. 10. 27. 12:39 | Posted in Framework/Spring Security
반응형

Intoduction


Spring Security 사용 중에 "사용자의 로그인 시간과 로그아웃 시간을 기록해야 한다" 라는 임무가 떨어졌다 -_-


문제는 이 로그아웃 이라는게 약간 골치 아프다.. 사용자가 직접 로그아웃 버튼을 클릭해서 로그아웃을 한다면 나이스 하지만...


대부분은 용무가 끝나면 그냥 브라우저를 끄거다 바로 다른 사이트로 넘어가게 된다. 이 경우에는 어떻게 하지?


개발자의 눈으로 보면 로그아웃 == 세션 만료로 볼 수 있다.


다른 사이트로 가거나 브라우저를 꺼버리게 되면 WAS의 기준으로 일정 시간이 지나세션이 만료되게 된다.


이 세션 만료를 캐취해서 처리를 하는 방법을 알아보자.




Using HttpSessionListener


서블릿의 세션 리스너를 이용한 방법이다.


아래와 같이 HttpSessionListener 인터페이스를 구현한 클래스를 만들고 web.xml 에 등록하면 된다.


스프링 시큐리티를 사용하지 않는다면 아래의 방법으로 처리하면 된다.


java


package session.destory.servlet;

import java.util.Date;

import javax.servlet.http.HttpSession;
import javax.servlet.http.HttpSessionEvent;
import javax.servlet.http.HttpSessionListener;

import org.springframework.context.ApplicationContext;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContext;
import org.springframework.security.web.context.HttpSessionSecurityContextRepository;
import org.springframework.web.context.support.WebApplicationContextUtils;

import session.destory.entity.LoginHistory;
import session.destory.security.LoginToken;
import session.destory.service.LoginHistoryService;

public class SessionManagerListener implements HttpSessionListener {

	@Override
	public void sessionCreated(HttpSessionEvent se) {
		// nothing
	}

	@Override
	public void sessionDestroyed(HttpSessionEvent se) {
		// session
		HttpSession session = se.getSession();
		// spring web application context
		ApplicationContext context = WebApplicationContextUtils
			.getWebApplicationContext(se.getSession().getServletContext());
		// security context
		SecurityContext sc = (SecurityContext) session
			.getAttribute(HttpSessionSecurityContextRepository.SPRING_SECURITY_CONTEXT_KEY);
		if (sc != null) {
			// authentication
			Authentication auth = sc.getAuthentication();
			// login history token
			LoginToken loginToken = (LoginToken) auth.getPrincipal();
			LoginHistory lh = loginToken.getHistory();
			lh.setLogoutDate(new Date());
			// update
			LoginHistoryService loginHistoryService = context
				.getBean(LoginHistoryService.class);
			loginHistoryService.modify(lh);
		}

	}

}


web.xml


<!-- servlet listener --> <listener> <listener-class>session.destory.servlet.SessionManagerListener</listener-class> </listener>


리스너에서 수동적으로 스프링 컨택스트와 시큐리티 컨텍스를 꺼내는게 맘에 안드는군... -_-/




Using HttpSessionEventPublisher


이번에는 스프링 시큐리티에서 지원하는 방법으로 해보겠다~!


ApplicationListener<SessionDestroyedEvent> 인터페이스를 구현한 클래스를 만들고 web.xml 에는 HttpSessionEventPublisher 클래스를 등록한다.


java


public class SessionDestoryListener implements ApplicationListener<SessionDestroyedEvent> {

	private LoginHistoryService loginHistoryService;

	@Autowired
	public void setLoginHistoryService(LoginHistoryService loginHistoryService) {
		this.loginHistoryService = loginHistoryService;
	}

	@Override
	public void onApplicationEvent(SessionDestroyedEvent event) {

		List<SecurityContext> contexts = event.getSecurityContexts();
		if (contexts.isEmpty() == false) {
			for (SecurityContext ctx : contexts) {
				Authentication atuh = ctx.getAuthentication().getPrincipal();
				// ...
			}
		}

	}

}

 

spring.xml

 

<beans ...>
	<bean id="sessionDestoryListener" class="session.destory.security.SessionDestoryListener" />
</beans>

 

web.xml


<!-- spring security event -->
<listener>
	<listener-class>org.springframework.security.web.session.HttpSessionEventPublisher</listener-class>
</listener>


뭔가 스프링스러워진 것 같다 -_-v


- 로그인 처리



- 로그아웃 처리






아래는 샘플 소스다. Tomcat 7, WebLogic 12c 에서 테스트 해봄.


war - 실행해 볼 수 있는 war 파일. 7-zip 으로 분할 압축함.


session.destory.zip.001


session.destory.zip.002



src.zip - Maven 구조로 만들어진 소스 압축함.


session.destory.src.zip


 

... 소스 만들고 나니까 오타가 있네.... Destory → Destroy


반응형

'Framework > Spring Security' 카테고리의 다른 글

AJAX Login with Spring Security  (7) 2013.12.12
MySql Password Encoder  (0) 2013.09.21
//

MySql Password Encoder

Posted at 2013. 9. 21. 19:24 | Posted in Framework/Spring Security
반응형
개인적으로 자주 사용하는 Spring Security Password Encoder 입니다.

MySql 의 password() 펑션 알고리즘 사용합니다.

import java.security.GeneralSecurityException; import java.security.MessageDigest; import org.springframework.security.crypto.password.PasswordEncoder; public class MySqlPasswordEncoder implements PasswordEncoder { @Override public String encode(CharSequence rawPassword) { if (rawPassword == null) { throw new NullPointerException(); } byte[] bpara = new byte[rawPassword.length()]; byte[] rethash; int i; for (i = 0; i < rawPassword.length(); i++) bpara[i] = (byte) (rawPassword.charAt(i) & 0xff); try { MessageDigest sha1er = MessageDigest.getInstance("SHA1"); rethash = sha1er.digest(bpara); // stage1 rethash = sha1er.digest(rethash); // stage2 } catch (GeneralSecurityException e) { throw new RuntimeException(e); } StringBuffer r = new StringBuffer(41); r.append("*"); for (i = 0; i < rethash.length; i++) { String x = Integer.toHexString(rethash[i] & 0xff).toUpperCase(); if (x.length() < 2) r.append("0"); r.append(x); } return r.toString(); } @Override public boolean matches(CharSequence rawPassword, String encodedPassword) { if (encodedPassword == null || rawPassword == null) { return false; } if (!encodedPassword.equals(encode(rawPassword))) { return false; } return true; } }


적용 예시..

	<security:authentication-manager alias="authenticationManager">
		<security:authentication-provider user-service-ref="userService">
			<security:password-encoder ref="passwordEncoder" />
		</security:authentication-provider>
	</security:authentication-manager>

	<bean id="passwordEncoder" class="MySqlPasswordEncoder">


반응형

'Framework > Spring Security' 카테고리의 다른 글

AJAX Login with Spring Security  (7) 2013.12.12
Spring Security Session Destroy  (4) 2013.10.27
//
반응형

Introduce



웹프로젝트 생성 후 추가적으로 해야할 작업을 정리합니다.



프로젝트를 생성하면 아래와 같은 구조로 생성이 됩니다.





Create Resource Folder


기본적인 메이븐 구조에 맞게 있어야 하는 폴더가 안 만들어져 있습니다.


main/java

main/resources <- 이미 생성되어 있음

test/java

test/resources


폴더를 생성하고


main/webapp


폴더는 삭제합니다.



Project Preperties 로 들어갑니다. (프로젝트에서 마우스 오른쪽 버튼 - Properties)


Java Build Path에 3개는 이미 추가되어 있으니(폴더도 없었으면서 -_-)  한개 빠진 폴더를 추가합니다.


Add Folder 버튼 클릭



남은 하나의 폴더(test/resources)를 추가합니다.



OK 후 Java  Resources 쪽에 보면 4개의 폴더가 적용되어 있습니다.





Change JRE System Library


저는 JDK 1.5 가 없습니다. 1.6 을 사용합니다. 하지만 메이븐 프로젝트를 만들면 기본적으로 빌드를 1.5로 하게 되어 있습니다. 이클립스에서 알아서 1.6으로 해주지만 경고가 표시됩니다.



Project Facets 설정 에서 1.6 으로 바꿔줄 수 있지면 나중에 pom.xml 설정을 바꾸다 보면 다시 원상 복귀 됩니다.


해결방법은 pom.xml 에서 플러그인 부분을 아래와 같이 추가 후 저장합니다.


<build>

<finalName>maven.project</finalName>


<!-- 이거 -->

<plugins>

<plugin>

<groupId>org.apache.maven.plugins</groupId>

<artifactId>maven-compiler-plugin</artifactId>

<version>3.1</version>

<configuration>

<source>1.6</source>

<target>1.6</target>

</configuration>

</plugin>

</plugins>

</build>

</project>


그러면 아래와 같이 에러가 나게 됩니다.



하라는대로 합니다. ㅎㅎ 프로젝트 오른쪽 버튼 클릭후 Maven - Update Project.



그럼 메이븐 프로젝트 선택하고 나옵니다. 현재 작업중인 프로젝트를 선택합니다.


그러면 이제 1.6 으로 돌아가게 됩니다. 물론 경고도 사라집니다.




Change Web Resource Location


처음 메이븐 웹 프로젝트를 만들면 웹 관련 폴더는 {프로젝트 루트}/src/main/webapp 에 있습니다.


이게 좀 소스 찾아 들어가려면 귀찮습니다. -_-;; 이 부분을 {프로젝트 루트}/webapp 로 변경하겠습니다.


이미 위에서 src/main/webapp 폴더는 삭제했습니다. 삭제하지 안았다면 삭제합니다.


프로젝트 바로 아래에 webapp, WEB-INF 폴더, web.xml(servlet 2.5) 파일을 생성합니다.



webapp 경로가 바뀌었으니 pom.xml 파일에 maven-war-plugin 설정을 추가합니다.


<plugin>

<groupId>org.apache.maven.plugins</groupId>

<artifactId>maven-war-plugin</artifactId>

<version>2.4</version>

<configuration>

<warSourceDirectory>webapp</warSourceDirectory>

</configuration>

</plugin>

</plugins>

</build>

</project>


수정 후 저장을 하면 다시 Update Project 하라고 에러가 납니다.


아직 추가 설정을 더 해줘야합니다!!


Navigator 뷰에서 프로젝트를 본 후 .settings 폴더에서 두개의 파일을 수정합니다.



.jsdtscope - 블럭된 부분을 삭제합니다.


<?xml version="1.0" encoding="UTF-8"?>

<classpath>

<classpathentry kind="src" path="src/main/webapp"/>

<classpathentry kind="con" path="org.eclipse.wst.jsdt.launching.JRE_CONTAINER"/>

<classpathentry kind="con" path="org.eclipse.wst.jsdt.launching.WebProject">

<attributes>

<attribute name="hide" value="true"/>

</attributes>

</classpathentry>

<classpathentry kind="con" path="org.eclipse.wst.jsdt.launching.baseBrowserLibrary"/>

<classpathentry kind="output" path=""/>

</classpath>


org.eclipse.wst.common.project.facet.core.xml - jst.web 버전을 2.5로 변경합니다. (자신의 web.xml 따라서 변경)


<?xml version="1.0" encoding="UTF-8"?>

<faceted-project>

  <fixed facet="wst.jsdt.web"/>

  <installed facet="jst.web" version="2.5"/>

  <installed facet="wst.jsdt.web" version="1.0"/>

  <installed facet="java" version="1.6"/>

</faceted-project>


모두 저장 후 Project Explorer 뷰로 이동 후 Maven - Update Project


프로젝트를 보면 Deployment Descriptor 가 표시 되었습니다.



Maven Install 이나 War Export 해보면 정상적인 구조로 말리는 걸 확인할 수 있네요~


반응형
//

SpringDM Test Project - hello.osgi

Posted at 2013. 4. 7. 21:16 | Posted in Framework/Spring OSGi
반응형

SpringDM 환경 구성 후 이것 저것 잘 되는지 테스트하기 위한 기본 샘플을 만들어 봤습니다.


처음에 잘 못하니까 무지하게 고생 하네요;;


일반적인 스프링 MVC 구조로 보면 아래와 같습니다.



하나의 프로젝트에 컨트롤러/서비스/퍼시스턴스(마이바티스) 등등 모든 구성이 다 되어있죠 ㅎㅎ


이 구조를 각 레이어 기준으로 OSGi 구조로 변경해 보았습니다.



web/service/persistent/core 4개의 번들로 나누어 만들어 봤습니다.


아래 그림은 그냥 사용할 데이터베이스 스키마입니다.



이제 하나하나 번들화 해서 작업 해봐야 겠습니다.


일단 <context:component-scan /> 안되네요... classpath*: 관련된 것은 다 안되는 듯 합니다... 아닌가?




작업하면서 추가한 번들 목록입니다.



jar 파일 및 소스 파일입니다.


hello.osgi.jar.zip


hello.osgi.src.zip


jar 파일은 개발환경에서만 해서 실제 환경에 올리면 될지 모르겠네요 ;;;


반응형

'Framework > Spring OSGi' 카테고리의 다른 글

OSGi + Tomcat 6 + SpringDM 플랫폼 환경 구성  (0) 2013.03.25
//
반응형

이제서야 OSGi 에 관심이 생겨서 뭔가를 만들어보기 위해서 OSGi + Tomcat 6 + SpringDM[각주:1] 개발 환경을 구성해 봅니다.


Eclipse Indigo + Maven Integration for Eclipse





Create Project


File - New - Other... → Maven - Maven Project




[maven-archetype-quickstart] 선택합니다.



[Group Id], [Artifact Id] 아무거나 입력 합니다. -_-;;


[Package] 는 필요가 없습니다.





Create Target Definition


File - New - Other... → Plug-in Development - Target Definition



[Parent Folder]는 전에 만든 "spring-dm-platform" 프로젝트를 선택합니다.


File name: spring-dm-tomcat.target

Initalize the target definition with: Nothing



lib 폴더를 하나 생성합니다.


target 폴더는 메이븐 라이브러리가 복사되는 곳이고, lib 폴더는 메이븐으로 구하지 못한 라이브러를 넣어둘 것입니다.



spring-dm-tomcat.target 파일을 열어 편집합니다.


Target: SpringDM with Tomcat


오른쪽 Add... 버튼을 클릭해서 Locations 을 추가합니다.



Directory 선택



Variables.. 를 이용해서 현재 프로젝트 경로(project_loc)를 얻어내고 하위 폴더 target 을 입력합니다. (그냥 입력해도 됨..)



같은 방법으로 lib 폴더도 선택합니다.



파일 저장 후 Windows - Preferences - Plug-in Development - Target Platform


SpringDM with Tomcat 을 선택합니다.






Running OSGi Framework


이제 한번 실행시켜 봅시다.


Run - Run Configurations...



왼쪽 메뉴 중에 OSGi Framework 에서 오른쪽 버튼 클릭 - New



이름 적당히 지어 주시고 Run 을 클릭해 봅시다.



org.eclipse.osgi 플러그인이 없다고 에러가 납니다.





Setting Osgi Framework on Maven


이제 메이븐으로 OSGi Framework 라이브러리를 세팅합시다.


pom.xml 파일을 열어 아래와 같이 수정 합니다.



메이븐에 대해서 설명하지는 않겠습니다. -_-;;


위의 라이브러리 저장소(repository)는 SpringSource Repository(FAQ)에서 확인할 수 있습니다.


이제 SpringSource Repository에서 번들(라이브러리)를 하나씩 찾아서 넣어야 합니다. ㅠ_ㅠ;;


검색하는 방법은 [Advanced Search]나 오른쪽 상단 [Quick Search] 을 이용해서 검색하면 됩니다.



일단 OSGi Framework 실행 했을 때 없다고한 플러그인 라이브러리를 검색해봅시다.


검색해서 나온 버전 중 가장 최신 버전을 선택합니다.



중간에 보면 Maven 부분이 있습니다. 이걸 복사해서 <dependencies> </dependencies> 사이에 붙여넣기 하면 됩니다. ㅎㅎ



<dependencies>

<!-- 라이브러리 설정 -->

<dependency>

<groupId>org.eclipse.osgi</groupId>

<artifactId>org.eclipse.osgi</artifactId>

<version>3.7.1.R37x_v20110808-1106</version>

</dependency>

</dependencies>


저장 후 pom.xml 파일에서 오른쪽버튼 클릭 후 Run As - Maven install 클릭 합니다.



그러면 target 폴더에 jar 파일이 복사가 됩니다.



이제 spring-dm-tomcat.target 파일을 열어봅시다.


아래와 같이 번들이 추가 되어있습니다.


※ 메이븐을 이용해서 빠르게 라이브러리를 바꿔치기 하다보면 번들(라이브러리)이 갱신이 안될 때가 있는데 그냥 파일 닫았다가 다시 열어주면 갱신이 되더군요....



맨위의 타이틀 오른쪽에 "Set as Target Platform"을 클릭해야 실제 적용이 됩니다. 주의!



이제 다시 Run Configurations 로 가봅시다. 추가한 번들이 올라와 있습니다. 선택 후 Run!



콘솔 창을 보면 OSGi Framework 가 실행되었습니다. ㅠ_ㅠ



이제 OSGi 할 준비가 된 것입니다. ㅋㅋ


톰켓이나 웹로직 같이 끄려면 빨간 버튼(Terminate)을 이용해서 끄면 됩니다.


나중에 톰켓를 띄우게 될텐데 OSGi Framework 한번 실행한 상태에서 한번 실행하면 톰켓 포트 충돌로 인한 에러를 볼 수 있습니다.




Setting SpringDM on Maven


바로 SpringDM을 적용해 봅시다!!


SpringSource Repository 에서 "spring", "spring-osgi", "slf4j" 를 검색해서 최신 버전을 적용 합니다.



이제 pom.xml 에서 마우스 오른쪽 버튼 - Run As - Maven install 해서 jar 파일을 target 에 생기게 합니다.


그리고 spring-dm-tomcat.target 파일을 열어서 ${project_loc}/target 폴더에 플러그인 갯수가 늘어났는지 확인합니다.


만약 파일은 존재하는데 변경이 되지 안았다면 spring-dm-tomcat.target 파일 닫고 바로 다시 열어주면 카운트가 변경 되어 있을 겁니다.



상단 오른쪽의 "Set as Target Platform" 링크를 클릭하여 라이브러리를 적용시켜 줍니다.



Run Configurations 창으로 가서 추가된 번들(또는 라이브러리)를 선택합니다.


그 후 오른쪽 아래 "Validate Bundles" 버튼을 클릭합니다. 버튼명 그대로 번들들의 유효성을 검사하게 됩니다.



아래와 같이 어떤 라이브러리에서 필요한 라이브러리가 빠져있는지 나옵니다.



웹에 관련된 부분은 바로 이어서 할 것이므로 일단 이 번들은 빼고 구동(Run)합니다.



아래와 같이 나오면 성공! 웹쪽을 뺀 SpringDM 까지 올라갔습니다.


osgi> log4j:WARN No appenders could be found for logger (org.springframework.osgi.extender.internal.activator.ContextLoaderListener).

log4j:WARN Please initialize the log4j system properly.

log4j:WARN See http://logging.apache.org/log4j/1.2/faq.html#noconfig for more info.


osgi> ss


Framework is launched.


id State       Bundle

0 ACTIVE      org.eclipse.osgi_3.7.1.R37x_v20110808-1106

1 ACTIVE      org.springframework.transaction_3.2.1.RELEASE

2 ACTIVE      org.springframework.osgi.core_1.2.1

3 ACTIVE      org.springframework.osgi.extensions.annotations_1.2.1

4 ACTIVE      com.springsource.org.apache.log4j_1.2.16

5 ACTIVE      org.springframework.jdbc_3.2.1.RELEASE

6 ACTIVE      org.springframework.oxm_3.2.1.RELEASE

7 ACTIVE      org.springframework.aspects_3.2.1.RELEASE

8 ACTIVE      com.springsource.org.apache.commons.logging_1.1.1

9 ACTIVE      org.springframework.context.support_3.2.1.RELEASE

10 ACTIVE      org.springframework.core_3.2.1.RELEASE

11 ACTIVE      org.springframework.beans_3.2.1.RELEASE

12 ACTIVE      org.springframework.context_3.2.1.RELEASE

13 ACTIVE      org.springframework.aop_3.2.1.RELEASE

14 RESOLVED    com.springsource.slf4j.log4j_1.6.1

           Master=17

15 ACTIVE      org.springframework.expression_3.2.1.RELEASE

16 ACTIVE      org.springframework.orm_3.2.1.RELEASE

17 ACTIVE      com.springsource.slf4j.api_1.6.1

           Fragments=14

18 ACTIVE      com.springsource.org.aopalliance_1.0.0

19 ACTIVE      org.springframework.osgi.extender_1.2.1

20 ACTIVE      org.springframework.osgi.io_1.2.1


osgi> 




Configuration Log4j


OSGi Framework 를 실행 하면 아래와 같은 경고가 나오게 되는데 흔하디 흔한 log4j.properties / log4j.xml 파일 못 찾는 경고입니다.


log4j:WARN No appenders could be found for logger (org.springframework.osgi.extender.internal.activator.ContextLoaderListener).

log4j:WARN Please initialize the log4j system properly.

log4j:WARN See http://logging.apache.org/log4j/1.2/faq.html#noconfig for more info.


log4j.xml


첨부한 log4j.xml 파일을 프로젝트 폴더에 복사합니다.



Run Configurations 창의 Arguments 탭으로 이동합니다.


VM arguments 부분에 -Dlog4j.debug=true -Dlog4j.configuration=file:log4j.xml 를 추가합니다.


Working directory 부분은 현재 자기의 프로젝트 경로로 바꿔줍니다.



Workspace... 버튼 클릭 후 spring-dm-platform 프로젝트 폴더를 선택하면 됩니다.



이제 OSGi Framework 를 실행하면 로그가 출력 됩니다.


osgi> log4j: Using URL [file:log4j.xml] for automatic log4j configuration.

log4j: Preferred configurator class: org.apache.log4j.xml.DOMConfigurator

log4j: System property is :null

log4j: Standard DocumentBuilderFactory search succeded.

log4j: DocumentBuilderFactory is: com.sun.org.apache.xerces.internal.jaxp.DocumentBuilderFactoryImpl

log4j: debug attribute= "null".

log4j: Ignoring debug attribute.

log4j: reset attribute= "false".

log4j: Threshold ="null".

log4j: Retreiving an instance of org.apache.log4j.Logger.

log4j: Setting [org.springframework.osgi.web.tomcat.internal] additivity to [false].

log4j: Level value for org.springframework.osgi.web.tomcat.internal is  [debug].

log4j: org.springframework.osgi.web.tomcat.internal level set to DEBUG

log4j: Class name: [org.apache.log4j.ConsoleAppender]

log4j: Parsing layout of class: "org.apache.log4j.PatternLayout"

log4j: Setting property [conversionPattern] to [%d _ %t _ %p _ %c _ %m%n].

log4j: Adding appender named [console] to category [org.springframework.osgi.web.tomcat.internal].

log4j: Retreiving an instance of org.apache.log4j.Logger.

log4j: Setting [org.springframework] additivity to [false].

log4j: Level value for org.springframework is  [info].

log4j: org.springframework level set to INFO

log4j: Adding appender named [console] to category [org.springframework].

log4j: Level value for root is  [warn].

log4j: root level set to WARN

log4j: Adding appender named [console] to category [root].

2013-03-27 00:54:43,505 _ Start Level Event Dispatcher _ INFO

_ org.springframework.osgi.extender.internal.activator.ContextLoaderListener

_ Starting [org.springframework.osgi.extender] bundle v.[1.2.1]

2013-03-27 00:54:43,545 _ Start Level Event Dispatcher _ INFO

_ org.springframework.osgi.extender.internal.support.ExtenderConfiguration

_ No custom extender configuration detected; using defaults...

2013-03-27 00:54:43,550 _ Start Level Event Dispatcher _ INFO

_ org.springframework.scheduling.timer.TimerTaskExecutor _ Initializing Timer


osgi> 


여기까지 했으면 이제 웹을 제외한 SpringDM을 사용할 준비가 되었습니다.




Setting Web Platform


이제 거의 다 왔습니다.... 후우~.. 이제 웹도 되게 합시다!



Run Configurations 창에서 전 단계에서 선택하지 않았던 4개의 웹 관련 번들을 선택 후 검사합니다. (Validate Bundles 버튼)



아래에 나온 없는 패키지가 포함된 번들을 찾아줘야 합니다.



일단 서블릿 2.4 버전을 찾아서 넣어줍시다.



이렇게 추가된 라이브러리를 적용한 후 다시 Run Configurations 창을 엽니다.


Add Required Bundles 버튼을 클릭하면 필요한 라이브러리를 자동으로 선택 해줍니다. (만약 있다면..)



이제 번들 유효성 검사를 해보면 아무 이상이 없다고 합니다.



실행! 잘 되는듯 하다가 에러가 나는군요...


2013-03-28 23:20:49,878 _ WebExtender-Init _ INFO

_ org.springframework.osgi.web.extender.internal.activator.WarListenerConfiguration

_ No custom extender configuration detected; using defaults...

Exception in thread "WebExtender-Init" java.lang.NoClassDefFoundError: org/apache/catalina/Loader

at o.s.osgi.web.extender.internal.activator.WarListenerConfiguration.createDefaultWarDeployer(WarListenerConfiguration.java:194)

at org.springframework.osgi.web.extender.internal.activator.WarListenerConfiguration.<init>(WarListenerConfiguration.java:105)

at org.springframework.osgi.web.extender.internal.activator.WarLoaderListener$1.run(WarLoaderListener.java:361)

at java.lang.Thread.run(Unknown Source)

Caused by: java.lang.ClassNotFoundException: org.apache.catalina.Loader

at org.eclipse.osgi.internal.loader.BundleLoader.findClassInternal(BundleLoader.java:513)

at org.eclipse.osgi.internal.loader.BundleLoader.findClass(BundleLoader.java:429)

at org.eclipse.osgi.internal.loader.BundleLoader.findClass(BundleLoader.java:417)

at org.eclipse.osgi.internal.baseadaptor.DefaultClassLoader.loadClass(DefaultClassLoader.java:107)

at java.lang.ClassLoader.loadClass(Unknown Source)

... 4 more


톰켓 번들을 설치합시다.


내용이 너무 길어저 pom.xml 만 남깁니다.



번들(라이브러리) 적용 후 우효성 검사를 하면 이상이 없다고 합니다.


보면 서블릿 번들이이 2.4/2.5 두개가 있는데 이중에 한가지만 선택하면 되겠습니다.



이제 실행해 봅시다.


WebExtender-Init _ ERROR _ o.s.osgi.web.deployer.tomcat.TomcatWarDeployer _ No Catalina Service found, bailing out

o.s.osgi.service.ServiceUnavailableException: service matching filter=[(objectClass=org.apache.catalina.Service)] unavailable


WebExtender-Init _ ERROR _ o.s.osgi.web.extender.internal.activator.WarLoaderListener _ Cannot property start Spring DM WebExtender; stopping bundle...

o.s.osgi.OsgiException: Cannot create Tomcat deployer

Caused by: o.s.osgi.service.ServiceUnavailableException: service matching filter=[(objectClass=org.apache.catalina.Service)] unavailable


WebExtender-Init _ INFO _ org.springframework.scheduling.timer.TimerTaskExecutor _ Cancelling Timer


org.apache.catalina.Service 서비스를 찾을 수 없다. 즉 톰켓이 안 떠있다는 겁니다.


catalina.start.osgi-6.0.16-20080327.121306-4.jar


첨부한 번들을 lib 폴더에 복사해주고 적용 시킵니다. (이건 메이븐에 없더군요...)



마지막으로 실행!


Tomcat Catalina Start Thread _ INFO _ o.s.osgi.web.tomcat.internal.Activator _ Starting Apache Tomcat/6.0.32 ...

Tomcat Catalina Start Thread _ DEBUG _ o.s.osgi.web.tomcat.internal.Activator _ Reading default server configuration from  bundleresource://45.fwk1110027070/conf/embedded-server-defaults.properties

2013. 3. 28 오후 11:40:53 org.apache.catalina.startup.Embedded start

정보: Starting tomcat server

2013. 3. 28 오후 11:40:53 org.apache.catalina.core.StandardEngine start

정보: Starting Servlet Engine: Apache Tomcat/6.0.32

2013. 3. 28 오후 11:40:53 org.apache.coyote.http11.Http11Protocol init

정보: Initializing Coyote HTTP/1.1 on http-127.0.0.1-8080

2013. 3. 28 오후 11:40:53 org.apache.coyote.http11.Http11Protocol start

정보: Starting Coyote HTTP/1.1 on http-127.0.0.1-8080

Tomcat Catalina Start Thread _ INFO _ o.s.osgi.web.tomcat.internal.Activator _ Succesfully started Apache Tomcat/6.0.32

WebExtender-Init _ INFO _ o.s.osgi.service.importer.support.OsgiServiceProxyFactoryBean _ Found mandatory OSGi service for bean []

Tomcat Catalina Start Thread _ INFO _ o.s.osgi.web.tomcat.internal.Activator _ Published Apache Tomcat/6.0.32 as an OSGi service

WebExtender-Init _ INFO _ o.s.osgi.web.deployer.tomcat.TomcatWarDeployer _ Found service Catalina


후우... 성공!


web.xml


마지막으로 첨부한 web.xml 파일을 conf/web.xml 에 복사합니다. (그냥 톰켓에 있는 web.xml 입니다)



이 파일이 있어야 나중에 웹어플 배치했을 때 정상적으로 됩니다.


안그러면 배치했을 때 아래 로그와 함께 영원히 404를 볼 수 있습니다.


[ INFO - WebExtender-Init] org.springframework.osgi.web.extender.internal.activator.WarLoaderListener - hello.web (hello.web) is a WAR, scheduling war deployment on context path [/hello.web] (web.xml found at [bundleentry://11.fwk854535264/WEB-INF/web.xml])

2013. 3. 30 오전 2:03:35 org.apache.catalina.startup.ContextConfig defaultWebConfig

정보: No default web.xml

[ INFO - Timer-0] org.springframework.osgi.web.deployer.tomcat.TomcatWarDeployer - Successfully deployed bundle [hello.web (hello.web)] at [/hello.web] on server org.apache.catalina.startup.Embedded/1.0




Trouble Shooting


설정을 바꿨는데도 안바뀐다면?


Run Configurations 의 Settings 탭에서 "Clear the configuration area before launching"를 체크해주세요.



중간 중간 아래와 같은 에러가 난다면? 하라는 대로 하면 됩니다. -_-;;





마지막으로 최종 pom.xml 파일 첨부합니다.


pom.xml



  1. 구) Spring OSGi / 신) Spring Dynamic Modules [본문으로]
반응형

'Framework > Spring OSGi' 카테고리의 다른 글

SpringDM Test Project - hello.osgi  (0) 2013.04.07
//
반응형

아래와 같은 jsp 소스가 있습니다.


<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<%@ taglib prefix="form" uri="http://www.springframework.org/tags/form" %>
<!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>taglib</title>
</head>
<body>

</body>
</html>


네... 스프링 커스텀 태그를 사용하려고 합니다.


이 페이지를 불르면 톰켓 7에서는 아래와 같은 로그가 출력되고 정상적으로 작동됩니다. (다른 WAS는 모르겠슴 -_-/)


정보: At least one JAR was scanned for TLDs yet contained no TLDs. Enable debug logging for this logger for a complete list of JARs that were scanned but no TLDs were found in them. Skipping unneeded JARs during scanning can improve startup time and JSP compilation time.


뭐... 알아서 TLD 파일 찾았다는 거죠 ㄷㄷ;;;


하지만 웹로직 환경에서 페이지를 불르면 에러가 나게 됩니다.


index.jsp:2:5: No tag library could be found with this URI. Possible causes could be that the URI is incorrect, or that there were errors during parsing of the .tld file. 


<%@ taglib prefix="form" uri="http://www.springframework.org/tags/form" %>

        ^----^


이런 경우 tld 파일을 추출해서 적당한 곳에 넣은 후 web.xml 에 등록을 해줘야 합니다.


<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xmlns="http://java.sun.com/xml/ns/javaee" xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
	xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
	id="taglib" version="2.5">

	<welcome-file-list>
		<welcome-file>index.jsp</welcome-file>
	</welcome-file-list>

	<!-- 여기 -->
	<jsp-config>
		<taglib>
			<taglib-uri>http://www.springframework.org/tags/form</taglib-uri>
			<taglib-location>/WEB-INF/tlds/spring-form.tld</taglib-location>
		</taglib>
	</jsp-config>

</web-app>


꼭 웹로직 환경이 아니라도  tld 를 찾지 못한다고 하면 web.xml 에 위와 같이 추가해 주면 됩니다.


<taglib-location> 값에 jar 안의 경로를 직접 지정해줄 수 있으면 더 깔끔할텐데....


반응형
//