context:component-scan 에서 @Configuration
root-context 설정에서
context-component scan 에서 자꾸 스프링 MVC 설정(@Configuration으로 되어있는)을 읽어가서.. 조금 살펴 보던 중에...
@Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @Documented @Component public @interface Configuration {}
위에껄 발견...OTL
@Configuration 이 @Component를 가지고 있어서...@Configuration은 스캔되는게 당연하다. 제외 하고 싶으면 exclude 로 빼면 된다.
별것도 아니었는데 -ㅅ-;;
Spring Social 에서 제공하는 ProviderSignInController에 neo4j 활용하기
private RedirectView handleSignIn(Connection connection, NativeWebRequest request) { ListuserIds = usersConnectionRepository.findUserIdsWithConnection(connection); if (userIds.size() == 0) { ProviderSignInAttempt signInAttempt = new ProviderSignInAttempt(connection, connectionFactoryLocator, usersConnectionRepository); request.setAttribute(ProviderSignInAttempt.SESSION_ATTRIBUTE, signInAttempt, RequestAttributes.SCOPE_SESSION); return redirect(signUpUrl); } else if (userIds.size() == 1) { usersConnectionRepository.createConnectionRepository(userIds.get(0)).updateConnection(connection); String originalUrl = signInAdapter.signIn(userIds.get(0), connection, request); return originalUrl != null ? redirect(originalUrl) : redirect(postSignInUrl); } else { return redirect(URIBuilder.fromUri(signInUrl).queryParam("error", "multiple_users").build().toString()); } }
저기 보면 사용자 아이디를 조회 하는데 userConnectionRepository를 사용하는데, 구현체로 제공하는게 JdbcUsersConnectionRepository 이다.
neo4j를 사용하고 있는중이라 당연히!! 사용할 수가 없어서 간단하게 findUserIdsWithConnection 만 구현하는 Neo4jUsersConnectionRepository 만들어서 사용.
Neo4j cypher 쿼리 팁 들
http://lolstats.kr
스프링 배치 2.2.0 릴리즈
회사에 간단히 포스팅한 것.
2, 3번 변경점이 굉장히 마음에 든다 :)
주요 변경점으론
1. Spring Data 지원
-> Spring Data는 스프링에서 NoSQL 관련 데이터베이스들 관련해서 쉽게 개발하기 위해 생성된 프로젝트 인데. 지원이 된다고 합니다.
Neo4j나 MongoDB를 지원하는 ItemReader, Writer 등등을 지원 합니다.
2. 자바 설정 지원
-> 스프링 3.1 버전 부터였나.. xml 설정 보다는 자바 설정을 더 추천하고 있는데요.
이번 스프링 배치 버전부터 자바 설정을 지원 합니다!!
기존에 XML로 만들었던 설정들을 자바 설정을 통해서 지원이 되기때문에.. XML 보느라 눈 빠지는 일이 줄어들것 같네요.
3. 유니크 하지 않는 Job 파라미터 지원
-> 이전 버전 스프링 배치 사용해보신분들은 아시겠지만, 배치 잡 실행시키기 위해서는 매번 다른 Job 파라미터들을 전달해 주어야 했습니다.
이번 버전부터는 매번 새로 생성하지 않아도 된다고 하네요.
이때문에 job repository 스키마가 일부분 변경되었고.. 다행히 마이그레이션 스크립트도 제공 한다고 합니다.
4. AMQP 지원
-> 메세징 서비스 미들웨어중 하나인 AMQP 지원 한다고 합니다.
5. SQLFIRE 지원
-> 이것도 지원 한다는데.. 뭔지 모르겠네요. :(
아시는분이 회신해주시면 좋을것 같습니다.
6. 스프링 상위 버전 지원
-> 3.2.x 버전 스프링을 지원합니다.(Hibernate 4 지원)
7. 기타 등등
-> 여러 버그와 성능 향상이 있었다고 하네요.
httpinvoker 사용하기.
요약하면, HTTP Invoker를 사용하면 직렬화를 이용해서 원격 호출을 지원할 수 있다.
클라이언트 쪽에서 사용하기 위해서는 당연히 원격 호출을 위한 인터페이스가 필요하다. 이 인터페이스를 포함한 파일들을 서비스를 노출하는 서버에서 제공 해야 된다.(maven으로 jar 파일로 묶어서 클라이언트에 배포, impl은 노출시킬 필요가 없고 필요한 인터페이스만 노출 하면 된다.)
위에 설정에서 처럼 httpInvokerProxy가 example.AccountService 타입의 bean을 생성해 주기 때문에(프록시로) 클라이언트로 사용할 때는
@Autowired private AccountService service; 형식으로 일반 Spring bean을 사용하는것처럼 사용하면 된다.
물론 내부적으로는 HTTP POST를 통해서 사용되는 거기 때문에 이를 인지하고 있어야 되고, 세부적인 설정이 필요 하다면 commons HttpClient를 httpInvokerRequestExecutor로 지정한 후에 여러가지 세팅을 지정할수 있다(timeout, connection pooling, 인증 등)
스프링에서 제공하는 remoting 기능중에 HTTP invokers 를 사용하는 방법이 있다.(위 문서를 간단히 번역, 날림 번역)
Burlap 과 Hessian을 사용하여 remoting 기능을 사용할 수 있지만 이 방법은 표준이 아닌 가벼운 프로토콜을 사용하여 자기들만의 직렬화 방식을 사용한다.
Spring Http Invokers HTTP를 통해 서비스를 노출하기 위해 자바 표준 직렬화 기능을 사용한다. 이것은 파라미터나 리턴타입이 복잡하고 Hessian 이나 Burlap의 직렬화 기법이 동작하지 않을때 큰 이점이 있다.
스프링은 J2SE에서 제공하는 표준 HTTP 호출이나 Commons HttpClient 를 사용한다. Commons HttpClient를 사용하는게 기능적으로 쉽고 좀 더 많은 이점이 있다. 이 링크를 참조 jakarta.apache.org/commons/httpclient
서비스 오브젝트 노출하기.
스프링 HttpInvoker는 org.springframework.remoting.httpinvoker.HttpInvokerServiceExporter를 제공한다.
AccountService 를 스프링 웹 MVC DispatcherServlet을 통해 노출시키기 위해서는 dispatcher의 application context에 아래와 같은 설정을 추가 해야 된다.
<bean name="/AccountService" class="org.springframework.remoting.httpinvoker.HttpInvokerServiceExporter"> <property name="service" ref="accountService"/> <property name="serviceInterface" value="example.AccountService"/> </bean>
이 방식은 DispathcerServlet의 표준 매핑 방식에 따라 매핑된다.
다른 방법은 HttpInvokerServiceExporter를 root application에 만들 수 있다(e.g 'WEB-INF/applicationContext.xml')
<bean name="accountExporter" class="org.springframework.remoting.httpinvoker.HttpInvokerServiceExporter"> <property name="service" ref="accountService"/> <property name="serviceInterface" value="example.AccountService"/> </bean>
추가적으로 이것과 통신하기 위한 exporter를 'web.xml'에 정의 해야 하고, 서블릿의 이름은 exporter와 일치 해야 한다.
<servlet> <servlet-name>accountExporter</servlet-name> <servlet-class>org.springframework.web.context.support.HttpRequestHandlerServlet</servlet-class> </servlet> <servlet-mapping> <servlet-name>accountExporter</servlet-name> <url-pattern>/remoting/AccountService</url-pattern> </servlet-mapping>
만약 서블릿 컨테이너를 사용하지 않고 Sun's Java 6를 사용한다면 내장된 HTTP servier implementation을 사용할수 있다.(이런것도 있었나) SimpleHttpServerFactoryBean과 함계 SImpleHttpInvokerServiceExporter를 아래의 예제처럼 사용하면 된다.
<bean name="accountExporter" class="org.springframework.remoting.httpinvoker.SimpleHttpInvokerServiceExporter"> <property name="service" ref="accountService"/> <property name="serviceInterface" value="example.AccountService"/> </bean> <bean id="httpServer" class="org.springframework.remoting.support.SimpleHttpServerFactoryBean"> <property name="contexts"> <util:map> <entry key="/remoting/AccountService" value-ref="accountExporter"/> </util:map> </property> <property name="port" value="8080" /> </bean>
서비스를 클라이언트와 연결하기.
프록시를 사용해서, 스프링은 당신의 요청을 HTTP POST requests로 변환하여 노출된 서비스를 가르키는 URL로 가게 해준다.
<bean id="httpInvokerProxy" class="org.springframework.remoting.httpinvoker.HttpInvokerProxyFactoryBean"> <property name="serviceUrl" value="http://remotehost:8080/remoting/AccountService"/> <property name="serviceInterface" value="example.AccountService"/> </bean>
전에 언급한 바와 같이 HTTP client는 원하는 것을 사용할 수 있다. J2SE HTTP 를 사용하는 HttpInvokerProxy가 기본으로 지정되어 있지만 httpInvokerRequestExecuttor 속성에 Commons HttpClient를 세팅해서 사용할 수도 있다.
<property name="httpInvokerRequestExecutor"> <bean class="org.springframework.remoting.httpinvoker.CommonsHttpInvokerRequestExecutor"/> </property>
기술을 선택할때의 고려사항
여기에서 제시된 각각의 기술은 단점이 있다.
RMI를 사용할때 RMI 트래픽을 터널링 하지 않는한 HTTP protocol을 통해 객체에 접근할수 없다. RMI는 완전한 객체 직렬화를 지원하는 상당히 무거운 프로토콜 이지만 정보를 통해 직렬화가 필요한 복합 데이터 모델을 사용할때 중요하다. 하지만, RMI-JRMPs는 Java 클라이언트에 종속적이 된다. 이것은 Java-to-Java간의 원격 호출 방식이다.
Spring에서 제공하는 HTTP invoker는 HTTP 기반의 원격호출을 사용하지만 자바 직렬화가 필요할때 사용할 수 있는 좋은 선택이다. 이것은 RMI invoker의 기본 구조를 공유하고 HTTP를 전송하기 위해서만 사용된다. HTTP invoker는 Java-to-Java 에 제한을 두진 않지만 클라이언트와 서버 둘다 스프링을 사용해야 된다.
Hessian 이나 Burlap은 이기종 환경에서 사용할때 큰 이점이 있다. 왜냐하면 이들은 명시적으로 Java Clinet가 아닌것도 사용가능하기 때문이다. 하지만 Java Clinet가 아닌것을 지원하는데는 여전히 제한이 있다. 알려진 이슈로는 늦은 초기화 콜렉션이 포함된 하이버네이트 객체를 직렬화 하는것에대한 문제가 있다. 만약 이런 구조의 데이터 모델을 가지고 있다면 Hessian의 대용으로 RMI나 HTTP invoker를 사용하는것을 고려할 수 있다.
JMS는 서비스들을 클러스터링하기에 용이하고 JMS 브로커를 통해 로드 벨런싱을 하게 하거나 자동 오류극복을 하게 할 수 있다. 기본적으로 JMS를 사용할때 자바의 직렬화를 사용하도록 되어 있지만 JMS provider는 XStream을 구현하거나 다른 방식을 구현한 것처럼 다른 메커니즘을 가진 방식을 사용할 수 있다.
마지막으로 가장 적게 사용되는건 아니지만 EJB는 RMI를 넘어서는 표준 role-based 인증민 인가, 원격 트랜잭션 전파를 지원하는 장점이 있다. 스프링의 핵심 기능에서 제공되지는 않지만 원격 보안 컨텍스트 전파를 지원하는 RMI Invoker나 HTTP Invoker를 통해 얻을 수 있다.
grails 배포 하기.
지금 만들어논 사이트 에서는
git pull 을 통해서 소스를 업데이트 한후에.
grails prod war 명령어를 통해 .war 파일을 만들고
war 명령어는 기본이 dev(devlopment) 이다.
이 파일을 tomcat/webapps/ 아래로 이동한 후 배포 하는 구조로 되어 있다.
스프링 시큐리티에서 인증 관련 체크 하는 부분.
스프링 시큐리티에선 패스워드 인코딩 방식을 몇가지 지원 하는데
로그인 시도시 입력한 패스워드를 인코딩 하고, db에 저장된 인코딩된 패스워드와 비교를 한다.
이것을 수행하는 부분은
org.springframework.security.authentication.dao.DaoAuthenticationProvider.additionalAuthenticationChecks(UserDetails, UsernamePasswordAuthenticationToken)
이 부분으로 여기서 인코딩 하는데 사용된 패스워드 인코더를 가지고 패스워드를 확인한다.
참고 할 것.
http://weblog.plexobject.com/?p=1420
참고참고
Neo4j 에서 Spring Security 사용하기..
그냥 일반 DB를 하이버네이트를 써서 스프링 시큐리티로 만든 User 클래스를 사용하면, 로그인도 잘 되고 아무 문제가 없는데
User 클래스를 Neo4j로 사용하면 로그인 하는데 문제가 발생한다.
User 클래스에는 시큐리티 플러그인이 만들어준 "beforeInsert", "beforeUpdate" 메서드가 있고, 여기서 password 인코딩을 해준다.
위 이벤트들은 GORM에서 지원해 주는 것들(http://grails.org/doc/latest/guide/GORM.html#eventsAutoTimestamping)
근데 Neo4j를 사용하면.. 그냥 list 를 호출하는데도 beforeUpdate 가 계속 호출되어, 패스워드 값이 여러번 변경된다..
무려 3번. 그냥 list를 호출하는데도.. -_-;; 이건 뭐야!!
저게 존재하는 이유가 패스워드 인코딩을 하기 위한거니..
해결 방법은.
User 클래스의 위의 두 메서드( beforeInsert, beforeUpdate)를 제거 하고, UserController에서 패스워드 인코딩 하는 내용을 넣어 주는 걸로 변경 함.
꼼수로.. 하이버네이트와 Neo4j를 같이 써볼려고 했는데..
Neo4j로 쓰는 도메인과 하이버네이트로 쓰는 도메인은.. Neo4j에서 참조가 안되는 관계로 포기.
예를 들어 Neo4j 는 '학교' 이고 하이버네이트가 '학생'일때 '학교'는 '학생'을 참조하고 있으면 에러남..
당연한 이야기긴 한데.. 뭔가 GORM에서 해결했을것 같은 느낌이 들어서 시도해봄...
아 그리고 User 클래스 만들어 주는 템플릿은 스프링 시큐리티 프러그인 내에 "Person.groovy.template"로 존재함.