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
Spring Security Session Destroy  (3) 2013.10.27
MySql Password Encoder  (0) 2013.09.21
  1. 궁금합니다!!
    안녕하세요
    세션 만료시 로그아웃 시간 입력을 위해 소스를 참고 했는데요,
    잘 안되는 부분이 있어서 질문좀 드리려고 합니다. ^^;
    spring security / spring 프레임웍을 사용중입니다.
    처음에 ApplicationListener<SessionDestroyedEvent>를 사용해서 구현했는데 SecurityContext 리스트가 계속 null이더라구요.
    결국 세션에서 얻어오는 event.getSession().getAttribute(HttpSessionSecurityContextRepository.SPRING_SECURITY_CONTEXT_KEY);
    방법으로 security context를 얻어왔습니다.
    궁금한게 getSecurityContexts() 로 리스트를 얻어올때는 null 이었는데 왜 세션에서 얻어오는 방법으로는 얻을수 있는지 모르겠습니다. 혹시 참고할만한 부분이나 조언을 해주시면 감사하겠습니다!
    • 2014.04.03 14:17 신고 [Edit/Del]
      1. 혹시 web.xml 에 리스너 등록 하셨죠?
      <listener>
      <listener-class>org.springframework.security.web.session.HttpSessionEventPublisher</listener-class>
      </listener>

      2. 잠깐 찾아보니 스프링 시큐리티 버전에 따른 문제가 있는 것 같기도 합니다. (아닐지도 모름 -_-ㄷㄷ)

      3. 저도 퇴근해서 이것저것 해보겠습니다.
  2. 궁금합니다!!
    넵 감사합니다!
    리스너는 등록한 상태구요, 사용중인 spring security 버전은 3.1.0입니다.
    getSecurityContexts()가 왜 계속 null인지.. 계속 다시 해봤는데 모르겠네요
    getSession().getAttribute(HttpSessionSecurityContextRepository.SPRING_SECURITY_CONTEXT_KEY) 과 내부적으로 같은 거라면 그냥 쓰려고 합니다.
    블로그 올려두신 내용들 많이 참고했습니다!! 감사합니다!

댓글 (Comment)

Name*

Password*

Link (Your Website)

Comment

SECRET | 비밀글로 남기기

이제서야 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
OSGi + Tomcat 6 + SpringDM 플랫폼 환경 구성  (0) 2013.03.25

댓글 (Comment)

Name*

Password*

Link (Your Website)

Comment

SECRET | 비밀글로 남기기

Spring Message Source from Database

Posted at 2013. 3. 3. 05:19 | Posted in Java+/Example

스프링을 이용한 다국어 처리를 데이터베이스를 이용해 어떻게 할까 이것저것 고민하다가 구현해 봤습니다.


개발 환경

 - Eclipse Indigo + Maven Integration (Sonatype, Inc.)

 - WebLogic 12c (Tomcat 7)


테이블은 아래와 같이 구성하였습니다.




특별한 것은 없고 여느 책에서나 나오는 스프링에서 MessageSource를 사용하여 다국어 메세지를 가져오는데 AbstractMessageSource 클래스를 상속 받아 따로 클래스를 만들어 봤습니다. (com.tistory.antop.framework.support.DatabaseMessageResource)




구동해 볼 수 있는 샘플을 만들어 봤습니다.


실제 메세지 리소스 부분만 구현하는 시간은 금방인데 하나의 완성품을 만들면서 더 많이 배우는군요~


Spring Framework 3.2.1

Mybatis 3.2 + Ehcache 2.6.3

Tiles 2.2.2 + Dynamic Tiles 1.2.1

HSQLDB 2.2.9

 

http://antop.nerv.kr/multi-lang/

 

데이터를 제외한 모든 부분을 다국어 처리 해봤습니다영어(기본), 한국어, 일어 데이터를 만들어 놨습니다. 구글 번역!


언어를 한글로 했을 때의 화면입니다.




아래는 언어를 영문으로 했을 때




자바스크립트 메세지 부분도 다국어 처리 해봤습니다. (/js/locale.js)






이클립스 프로젝트 파일입니다. 모든 파일 인코딩은 UTF-8 입니다.


multi-lang.zip


war 파일입니다. 16메가의 용량이 다 라이브러리군요... -_-/


multi-lang.zip.001

multi-lang.zip.002



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

LOGBack Configurator with JMX  (0) 2014.07.20
현지어로 언어명 보여주기  (0) 2014.02.09
2014년 도로명 주소 사용에 따른 우편번호 준비  (2) 2013.12.22
JSTL Custom Tag using Spring Beans  (0) 2013.12.01
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
  1. Jake Jeon
    이거 만들려고 했는데.... 이미 감사합니다.

댓글 (Comment)

Name*

Password*

Link (Your Website)

Comment

SECRET | 비밀글로 남기기

Infinite Routing DataSource

Posted at 2013. 1. 13. 18:54 | Posted in Java+/Example

스프링을 이용하여 몇개의 데이터소스를 정해놓고 라우팅을 하는 경우 AbstractRoutingDataSource를 이용할 수 있었습니다.


하지만 경우에 따라 접속해야하는 데이터베이스가 무한대일 경우?



그래서 이것저것 생각해 봤는데 -_-.... 


로그인시에 [고객 코드], [아이디], [비밀번호] 3가지를 입력 받습니다.


[고객코드]로 고객을 찾은 후 고객의 정보로 디비 정보를 만든 후 ThreadLocal에 저장합니다.


그 후 로그인 처리를 합니다.


로그인 시에 데이터소스를 사용할 때 ThreadLocal에 있는 디비 정보(DbInfo 객체)로 데이터소스를 만들거나 캐쉬에서 가져오게 합니다.


로그인 때에 처음 그 데이터베이스에 접속을 하게 되니까 RoutingDataSource 부분에서 데이터소스를 처음 만들게 될 것입니다.



로그인 처리 이후에는 인터셉터를 이용하여 세션에 있는 디비 정보를 ThreadLocal에 넣고 사용하면 되겠습니다.



이렇게 하면 똑같은 쿼리를 사용하면서 [코객코드]에 따라 다른 디비에 붙어서 사용할 수 있겠네요 ㅠ_ㅠ




Spring + Spring Security + Mybatis + SQLite 를 이용해서 샘플을 만들어 봤습니다.


아래와 같이 3개의 고객 스키마가 있습니다.



아래 테이블은 manager 스키마의 고객 관리 테이블 입니다.



비밀번호는 SALT 와의 조합으로 ARIA 알고리즘으로 저장됩니다.


SQLite는 파일 디비라서 직접적으로 아이디와 비밀번호는 사용하지 않습니다.




아래 테이블은 각각 고개 스키마에 있는 사용자 관리 테이블입니다. 암호는 MySQL 의 password() 알고리즘으로 저장됩니다.



계정은 각각 아래와 같이 들어 있습니다. 스키마명은 고객코드와 동일하게 했습니다.


cust001 (비밀번호: 12345 , welcome1!)



cust002 (비밀번호: jsnot)



cust003 (비밀번호: 11111)





핵심 소스 부분 몇 개 살펴보면...


package com.tistory.antop.service.common.impl;


public class LoginSoImpl implements LoginSo {


private AccountMapper accountMapper;


@Override

public Authentication login(String accountId, String username,

String password) {

// 고객 정보 조회

Account account = accountMapper.selectById(accountId);


// 없거나 사용 중지인 경우 예외

if (account == null || account.isUse() == false) {

throw new AccountNotFoundException();

}


// 디비 정보를 만듬

DbInfo dbInfo = new DbInfo();

dbInfo.setUsername(account.getId());

dbInfo.setEncPass(account.getEncPass());

dbInfo.setSalt(account.getSalt());


// ThreadLocal 에 저장

DbInfoContextHolder.set(dbInfo);


// Spring Security 인증

UsernamePasswordAuthenticationToken token = new UsernamePasswordAuthenticationToken(

username, password);

token.setDetails(account);


Authentication auth = authenticationManager.authenticate(token);


return auth;

}

}



아래 소스는 o.s.jdbc.datasource.lookup.AbstraceRoutingDataSource 클래스를 상속받아 구현한 RoutingDataSource 입니다.


캐쉬 역할을 하는 객체는 동기화를 위해 Hashtable을 사용하였습니다.


package com.tistory.antop.framework.datasource;


abstract public class RoutingDataSource extends AbstractRoutingDataSource {


private String url;

private String driverClassName;

private PasswordDecoder passwordDecoder;


private Map<DbInfo, DataSource> dataSources = new Hashtable<DbInfo, DataSource>();


@Override

protected DbInfo determineCurrentLookupKey() {

return DbInfoContextHolder.get();

}


@Override

protected DataSource determineTargetDataSource() {


DbInfo dbInfo = determineCurrentLookupKey();


if (dbInfo == null) {

throw new RoutingDataSourceException("데이터베이스 정보가 없습니다.");

}


try {

// 데이터소스가 없으면 생성

if (!contains(dbInfo)) {

// 비밀번호 복호화

String password = passwordDecoder.decodePassword(

dbInfo.getEncPass(), dbInfo.getSalt());

DataSource ds = createDataSource(dbInfo.getUsername(), password);

add(dbInfo, ds);

}


// 데이터소스 리턴

return get(dbInfo);

} catch (Exception e) {

throw new RoutingDataSourceException(e);

}


}


// 데이터 소스를 만드는 부분은 상속받은 객체가 함

abstract public DataSource createDataSource(String username, String password);

)




WAR 파일


바로 배치 가능한 WAR 파일입니다.


routing_datasource.zip.001


routing_datasource.zip.002



소스 ZIP 파일


이클립스 메이븐 구조의 프로젝트 소스입니다.


routing_datasource.zip



개발 테스트 : 톰켓 7, 웹로직 12c


배포 테스트 : 웹로직 12c




※ 질문!!!


웹로직에서 사용할 때 컨텍스트루트 다음에 "/" 가 붙느냐 안붙느냐에 따라서 스프링 시큐리티 필터를 타고 안타고 다른 결과가 나옵니다...


 URL  정상 처리 여부

 http://localhost:7001/routing_datasource/

 정상 처리 됨
 http://localhost:7001/routing_datasource

 스프링 시큐리티 필터를 타지 못하고 그냥 넘어가서 에러


톰켓에서는 "/" 상관없이 정상적으로 됨.... 이 부분 해결법 아시는분.. ㅠㅠ



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

LOGBack Configurator with JMX  (0) 2014.07.20
현지어로 언어명 보여주기  (0) 2014.02.09
2014년 도로명 주소 사용에 따른 우편번호 준비  (2) 2013.12.22
JSTL Custom Tag using Spring Beans  (0) 2013.12.01
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
  1. hsji
    아주 큰 도움이 되었습니다.

댓글 (Comment)

Name*

Password*

Link (Your Website)

Comment

SECRET | 비밀글로 남기기

Proftpd Manager

Posted at 2012. 12. 10. 05:11 | Posted in Mini Project

드디어 오랜기간의 방황을 깨고 마무리가 되었습니다. ㅠ_ㅠ


빠진 기능도 있고 버그도 있겠지만(?) 다음 과제(?)를 위해서 마무리~




소개...

 

Ubuntu 에 Proftpd 와 Mysql 을 이용해서 사용자 관리와 파일 전송 기록을 남길 수가 있습니다.

 

http://www.sysadminworld.com/2011/install-proftpd-with-mysql-backend-on-debian-ubuntu/

 

 

위와 같은 테이블로 관리를 하게 됩니다.

 

유저와 그룹을 관리하고 쌓이기만 하는 전송 기록을 볼 수 있는 웹어플을 한번 만들어 봤습니다.

 

※ 원래 데이터베이스는 MySQL 인데 샘플을 위해서 SQLite 로 데이터를 옮겼습니다.

 

 


 

사용한 기능...

 

Spring Framework 3

  - Core, AOP, MVC

 

Spring Security 3

  - AuthenticationManager 를 이용한 인증 처리

  - @PreAuthorize 어노테이션을 사용한 서비스단 메소드 보안

  - MySQL 의 password() 로직을 사용한 PasswordEncoder

 

MyBatis 3

  - List<String> ↔ 콤마 구분 문자열 타입 핸들러

  - Date ↔ datetime 타입 핸들러

 

Ext JS 4

  - 컴포넌트 동적 로딩 (필요한 클래스 js 파일을 그때그때 AJAX 로 로드)

  - 페이징 가능한 Tree Grid

  - 플러그인

    - FilterBar, MultiSorting, PagingToolbarResizer 등... -_-;




화면 설명... 


최초 페이지 로딩 후 전송 이력(History)에서 시작합니다. 데이터를 불러오려다가 로그인이 안되어 있어서 인증창이 뜹니다.



로그인이 성공하게 되면 데이터를 불러옵니다.


파일명과 접속 아이피는 문제가 될 소지가 있어서 이상하게 변환시켜 놨습니다 -_-;;;


 

아래는 사용자 관리 화면입니다. 사용자를 추가/수정/삭제 할수 있습니다.



사용자의 권한이 "관리자"만 그룹/사용자 정보를 관리할 수 있습니다. 그냥 "사용자"는 변경하려고 하면 권한이 없다고 나옵니다.



아래는 그룹 관리 화면입니다. 그룹을 추가/수정/삭제 하고, 사용자를 그룹에 포함/제외 시킬 수 있습니다.





소스 다운로드...


proftpd-maven.zip


위 파일은 이클립스 프로젝트 파일입니다. 메이븐 구조로 되어있습니다.


proftpd.war.zip.001


proftpd.war.zip.002



위 2개 파일은 war 파일을 7-zip으로 분할 압축 해놓은 겁니다.


압축 해제 후 나온 war 파일을 바로 배치 후 볼 수 있습니다. (소스 파일들도 들어있슴)

'Mini Project' 카테고리의 다른 글

Image Pyramid  (0) 2014.01.30
Proftpd Manager  (0) 2012.12.10
게시판 2.0  (5) 2011.07.27
Sudoku  (0) 2011.06.09
간단한 JAVA 실행기(?)  (1) 2011.03.21
Forecast Repository  (8) 2010.11.30
Sales History API  (2) 2010.05.27
Order Entry API  (0) 2010.05.13
Human Resources API  (0) 2010.05.10
게시판 1.0  (1) 2010.03.11
비밀번호 생성기(Generate Password)  (1) 2010.02.10

댓글 (Comment)

Name*

Password*

Link (Your Website)

Comment

SECRET | 비밀글로 남기기

Mybatis Type Handler

Posted at 2012. 9. 30. 12:49 | Posted in Java+/Example

마이바티스를 이용하여 자바 객체와 데이터베이스 테이블을 매핑할 때 서로 완전히 다른 타입의 것들을 사용할 때가 있습니다.

 

그 예로 대표적인 예가 자바의 Boolean 과 데이터베이스의 플래그 문자입니다.

 

 

오라클 같은경우 boolean 타입이 없기 때문에 보통 CHAR(1) 잡고 Y/N 값을 많이 사용합니다.

 

다른 경우는 1 or 0 을 사용하여 참/거짓, 사용/미사용, 차단/허용 같은 on/off 플래그 값을 사용합니다.

 

자바 

데이터베이스 

boolean

CHAR(1)

INTEGER  

참 / 사용

true

Y

 1 

거짓 / 미사용

false

N

 0 

 

org.apache.ibatis.type.TypeHandler 인터페이스를 이용해서 서로 다른 타입을 연결할 수 있습니다.

 

package com.tistory.antop.mybatis.handler;

import java.sql.CallableStatement;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;

import org.apache.ibatis.exceptions.PersistenceException;
import org.apache.ibatis.type.JdbcType;
import org.apache.ibatis.type.TypeHandler;
import org.apache.log4j.Logger;

public class StringYnTypeHandler implements TypeHandler<Boolean> {

    private Logger logger = Logger.getLogger(getClass());

    public Boolean getResult(ResultSet rs, String columnName) throws SQLException {
        String s = rs.getString(columnName);

        return parseBoolean(s);
    }

    public Boolean getResult(ResultSet rs, int columnIndex) throws SQLException {
        String s = rs.getString(columnIndex);

        return parseBoolean(s);
    }

    public Boolean getResult(CallableStatement cs, int columnIndex)
        throws SQLException {
        String s = cs.getString(columnIndex);

        return parseBoolean(s);
    }

    public void setParameter(PreparedStatement ps, int i, Boolean bool,
        JdbcType jdbcType) throws SQLException {

        ps.setString(i, parseString(bool));
    }

    // "Y" or "N" -> true or false
    private boolean parseBoolean(String s) {
        boolean bool = false;

        if (s == null) {
            return false;
        }

        s = s.trim().toUpperCase();

        if (s.length() == 0) {
            return false;
        }

        // allow "Y" or "N"
        if ("Y".equals(s) == false && "N".equals(s) == false) {
            throw new PersistenceException("value must be \"Y\" or \"N\".");
        }

        bool = "Y".equals(s);

        if (logger.isDebugEnabled()) {
            logger.debug("\"" + s + "\" -> " + bool);
        }

        return bool;
    }

    // true or false -> "Y" or "N"
    private String parseString(Boolean bool) {
        String s = (bool != null && bool == true) ? "Y" : "N";

        if (logger.isDebugEnabled()) {
            logger.debug(bool + " -> " + "\"" + s + "\"");
        }

        return s;
    }
}

 

이 클래스를 이용하면 자바에서는 boolean 타입을 사용하면 데이터베이스에서는 Y or N 값이 들어가게 됩니다.

 

mybatis.typehandler.zip

 

위 샘플은 메이븐 프로젝트 구조입니다.

 

샘플을 만드는 도중에 sqlite3 는 이상한게 TIMESTAMP 타입을 java.util.Date 클래스와 매핑 하면 들어갈 때는 그대로 들어가는데 조회할 때는 java.util.String 이 나오는군요....

 

그래서 TIMESTAMP 에 대한 핸들러 하나 더 만들었습니다.

 

테스트는 com.tistory.antop.mybatis.TypeHandlerTest 클래스 입니다. JUnit 입니다.

 

실행시 Run Configurations 중 VM 옵션에 이래와 같이 "-Dlog4j.configuration=config/properties/log4j.properties" 를 추가해 줘야 로그를 볼 수 있습니다.

 

 

 

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

LOGBack Configurator with JMX  (0) 2014.07.20
현지어로 언어명 보여주기  (0) 2014.02.09
2014년 도로명 주소 사용에 따른 우편번호 준비  (2) 2013.12.22
JSTL Custom Tag using Spring Beans  (0) 2013.12.01
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
  1. 좋은글 출처를 표시하고 블로그에 담아갑니다. ^^

댓글 (Comment)

Name*

Password*

Link (Your Website)

Comment

SECRET | 비밀글로 남기기

Sudoku

Posted at 2011. 6. 9. 15:33 | Posted in Mini Project
http://antop.nerv.kr/sudoku

※ 혹시나 해보고 에러 찾으면 댓글 달아주세요~ >.,<

드디어 완성이 되었네요...

서버단의 처리보다 화면(UI)의 구성에 더 투자했습니다. (화면 80%, 서버 20%)

서버단은 어노테이션(@) 방식으로 구현했습니다. 확실히 편하긴 하네요...

하지만 관리 측면에서는 xml 로 설정하는게 좋을 듯....?


프레임워크:

기타



소스 파일입니다.



테이블 스키마 파일입니다. (phpmyadmin 으로 export 함)



문제 데이터 입니다. (phpmyadmin)



'Mini Project' 카테고리의 다른 글

Image Pyramid  (0) 2014.01.30
Proftpd Manager  (0) 2012.12.10
게시판 2.0  (5) 2011.07.27
Sudoku  (0) 2011.06.09
간단한 JAVA 실행기(?)  (1) 2011.03.21
Forecast Repository  (8) 2010.11.30
Sales History API  (2) 2010.05.27
Order Entry API  (0) 2010.05.13
Human Resources API  (0) 2010.05.10
게시판 1.0  (1) 2010.03.11
비밀번호 생성기(Generate Password)  (1) 2010.02.10

댓글 (Comment)

Name*

Password*

Link (Your Website)

Comment

SECRET | 비밀글로 남기기

간단한 JAVA 실행기(?)

Posted at 2011. 3. 21. 18:00 | Posted in Mini Project
http://antop.nerv.kr/java

아주 간단한 JAVA 코드 테스트 하려고 이클립스 키고 프로젝트, 클래스 만들고 하기 귀찮아서 웹에서 바로 테스트 해볼 수 있는 걸 만들어 봤습니다.

크게 Ext JS 3.3.1Spring 3.0.5를 사용 했습니다.



아래 화면이 실행 화면입니다. ㅎㅎ



JAVA 에서 main 메소드 안에 들어갈 내용을 작성하면 됩니다.

public class ? {

	public static void main(String[] args) {
		// 작성
	}

}
작성 후 "실행" 버튼을 누르면 sysout으로 출력된 결과를 볼 수 있습니다.

"저장" 버튼은 작성한 소스를 java 파일로 다운로드 받을 수 있습니다.

"로그 삭제" 버튼은 아래의 Console 로그를 삭제합니다.
 


아래 그림은 화면 레이아웃입니다.

실제 기능 구현 하는것보다 레이아웃 잡는게 훨~씬 손이 많이 가는군요 ㅠㅠ




아래 그림은 간단한 흐름입니다. 



사용자가 소스를 작성해서 "실행" 버튼을 누르면 ExtJS에 의햇 비동기 폼 서브밋이 됩니다.

그럼 스프링 MVC 에서 받아서 java 파일 작성하고, 컴파일하고, 실행해서 결과 클리이언트에게 보내줍니다. (JSON)



파일 다운로드의 경우에는 java 파일 작성까지만 하고 바로 다운로드 받게 합니다.



짜잘한 기술로는

- textarea에서 탭키로 들여쓰기 가능
- AJAX 환경에서의 파일 다운로드(IFRAME을 생성하여 그곳으로 파일 다운로드 요청)





※ java.io 패키지는 사용 할 수 없게 했습니다. ㅎㅎ

'Mini Project' 카테고리의 다른 글

Image Pyramid  (0) 2014.01.30
Proftpd Manager  (0) 2012.12.10
게시판 2.0  (5) 2011.07.27
Sudoku  (0) 2011.06.09
간단한 JAVA 실행기(?)  (1) 2011.03.21
Forecast Repository  (8) 2010.11.30
Sales History API  (2) 2010.05.27
Order Entry API  (0) 2010.05.13
Human Resources API  (0) 2010.05.10
게시판 1.0  (1) 2010.03.11
비밀번호 생성기(Generate Password)  (1) 2010.02.10
  1. 비밀댓글입니다

댓글 (Comment)

Name*

Password*

Link (Your Website)

Comment

SECRET | 비밀글로 남기기

Using AUTO_INCREMENT keys

Posted at 2011. 3. 1. 16:02 | Posted in Java+/Example

http://dev.mysql.com/tech-resources/articles/autoincrement-with-connectorj.html

http://static.springsource.org/spring/docs/3.1.0.M1/spring-framework-reference/html/jdbc.html

MySQL 에는 AUTO_INCREMENT(이하 AI)라는 것이 있습니다. 오라클 디비의 시퀀스와 같이 자동으로 값이 증가하게 됩니다.

AI를 적용하려는 칼럼은 기본키K(Primary Key)여야 합니다.



Before JDBC API 3.0

JDBC API 3.0 이전에서는 INSERT 이후에 "select last_insert_id()" 쿼리를 날려서 추가된 AI 값을 가져와야 합니다.

Connection conn = null; PreparedStatement pstmt = null; ResultSet rs = null; // auto increment value int generatedKey = -1; try { conn = dataSource.getConnection(); // query String sql = "insert into sample_user (name, age) values (?, ?)"; pstmt = conn.prepareStatement(sql); pstmt.setString(1, user.getName()); pstmt.setInt(2, user.getAge()); // execute query pstmt.executeUpdate(); // query sql = "select last_insert_id()"; pstmt = conn.prepareStatement(sql); rs = pstmt.executeQuery(); if(rs.next()) { // auto increment value generatedKey = rs.getInt(1); } return generatedKey; } catch (Exception e) { throw e; } finally { // release rs, pstmt, conn }



After JDBC API 3.0

3.0 부터서는 "getGeneratedKeys()" 메소드를 이용하면 됩니다.

Connection conn = null; PreparedStatement pstmt = null; ResultSet rs = null; int generatedKey = -1; try { conn = dataSource.getConnection(); // query String sql = "insert into sample_user (name, age) values (?, ?)"; pstmt = conn.prepareStatement(sql, PreparedStatement.RETURN_GENERATED_KEYS); pstmt.setString(1, user.getName()); pstmt.setInt(2, user.getAge()); // execute query pstmt.executeUpdate(); rs = pstmt.getGeneratedKeys(); if (rs.next()) { generatedKey = rs.getInt(1); } return generatedKey; } catch (Exception e) { throw e; } finally { // release rs, pstmt, conn; }



Spring + JdbcDaoSupport

스프링과 연동하여 사용할 때에는 KeyHolder 인터페이스를 이용하면 됩니다.

@Override
public int create(User user) throws Exception {
	// query
	String sql = "insert into sample_user (name, age) values (:name, :age)";
	// parameter
	SqlParameterSource paramSource = new BeanPropertySqlParameterSource(user);
	// key holder
	KeyHolder generatedKeyHolder = new GeneratedKeyHolder();
	// execute query
	getNamedParameterJdbcTemplate().update(sql, paramSource, generatedKeyHolder);
	// auto increment
	return generatedKeyHolder.getKey().intValue();
}
※ KeyHolder 기능을 사용하려면 JDBC 드라이버가 JDBC 3.0을 지원해야 합니다.



샘플 소스입니다. (이클립스 Java Project 폴더 통째로 압축했씁니다. Buil Path 설정 해줘야 할지도...)

Spring 3.0 으로 되어있습니다. 간단한 DI만 이용...

kr/nerv/antop/config/spring.xml 파일에서 디비 설정 해줘야합니다.

테스트 클래스는 app.TestUserDao 입니다.

sample_mysql_ai.zip


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

LOGBack Configurator with JMX  (0) 2014.07.20
현지어로 언어명 보여주기  (0) 2014.02.09
2014년 도로명 주소 사용에 따른 우편번호 준비  (2) 2013.12.22
JSTL Custom Tag using Spring Beans  (0) 2013.12.01
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

댓글 (Comment)

Name*

Password*

Link (Your Website)

Comment

SECRET | 비밀글로 남기기

Eclipse + STS (SpringSource Tool Suite)

Posted at 2011. 1. 16. 23:16 | Posted in Tools/Eclipse
http://www.springsource.com/developer/sts
http://dist.springsource.com/release/STS/doc/STS-installation_instructions.pdf

스프링 3.0을 지원하는 IDE인 STS[각주:1]를 이클립스 플러그인으로 설치하는 방법입니다.

※ STS를 사용하려면 이클립스 3.5이거나 3.6이여야 합니다.



이클립스 메뉴에서 Window - Preferences 클릭



Install/Update - Available Software Site 에서 사이트를 전부 Disabled 시킵니다.

그리고 Import 버튼을 클릭합니다.



이리 구해놓은 xml 파일을 선택합니다. (전 3.6 버전)






Update Site 2개가 추가 되었습니다.

OK 버튼을 클릭합니다.



다시 메뉴에서 Help - Install New Software 클릭합니다.



Work with 에서 아래와 같이 추가한 사이트를 선택하고, 설치하고자 하는 플러그인을 선택합니다. (전 몰라서 다 선택 -_-)



설치 내역 확인 후 OK



라이센스 수락 후 Finish



설치중....



설치 완료 후 메뉴에서 Help - About Eclipse 클릭 해보면 아래와 같이 플러그인이 추가되었습니다.



  1. SpringSource Tool Suite [본문으로]

'Tools > Eclipse' 카테고리의 다른 글

Eclipse JVM 경로 지정하기  (0) 2016.09.13
Eclipse + SVN Plugin  (1) 2012.03.29
Maven Integration for Eclipse  (0) 2012.02.20
Eclipse + TPTP Remote  (1) 2012.01.20
Eclipse + TPTP (Eclipse Test & Performance Tools Platform)  (4) 2012.01.01
Eclipse + MyBatis Generator  (1) 2011.07.04
installation Oracle Enterprise Pack for Eclipse  (2) 2011.05.22
Eclipse + STS (SpringSource Tool Suite)  (1) 2011.01.16
Eclipse Plugins for JBoss technology  (1) 2011.01.11
Properties Editor  (1) 2009.11.17
UTF-8 설정  (0) 2009.11.16
tag: Eclipse, Spring, STS
  1. ^^
    감사합니다~

댓글 (Comment)

Name*

Password*

Link (Your Website)

Comment

SECRET | 비밀글로 남기기