데이터베이스에 값을 입력하는 중 에러가 발생했다...
정확히는 SQL과 파라미터를 조합해 로그를 찍어주는 곳에서... 

처음 보는 에러네?
로그...
### Error updating database.  Cause: java.lang.IndexOutOfBoundsException: No group 2

에러를 발생한 값...
2018-10-14 20:15 [DEBUG] ******* field value : {bcrypt}$2a$10$Rq1GlJlTVLXL/9GQLohX6.YGkn/yAHa.ZSOLekODrY.wVjTDvE1Sy

비밀번호를 암호화 하게되면 $가 문자열에 포함되게 되는데 이 $가 replace 쪽에서 문제를 일으키는 것... 
이럴 땐 좀 복잡하지만 돌아가야 한다...

public class ReplaceTest {
    public static void main(String[] args) {
         String sql = "This is password : ?";
         HashMap<String, String> field = new HashMap<String, String>();
         field.put("passwd", "{bcrypt}$2a$10$2ZnW3nw1AVN1jyKpa3uZJO0EhJ71jXDyahxbHTmlBu5MTsLPN3no2");
         
         String regex = "\\?";
         Pattern pattern = Pattern.compile(regex);
         Matcher matcher = pattern.matcher(sql);
         sql = matcher.replaceFirst(matcher.quoteReplacement("'" + field.get("passwd") + "'"));
         System.out.println(sql);
    }
}
result : This is password : '{bcrypt}$2a$10$2ZnW3nw1AVN1jyKpa3uZJO0EhJ71jXDyahxbHTmlBu5MTsLPN3no2' 






'백엔드 > Java' 카테고리의 다른 글

[Java] java.lang.IndexOutOfBoundsException: No group 2  (0) 2018.10.14
회원가입을 진행하던 중 HTTP 403 에러가 발생하였다.

Spring Security 3.2  이후 버전에서는 적절한 CSRF 토큰을 포함시켜주지 않으면 에러를 발생하게끔 되어있다.

관련 로그
2018-10-14 18:10 [DEBUG] /user/register.do at position 1 of 14 in additional filter chain; firing Filter: 'SecurityContextPersistenceFilter'
2018-10-14 18:10 [DEBUG] HttpSession returned null object for SPRING_SECURITY_CONTEXT
2018-10-14 18:10 [DEBUG] No SecurityContext was available from the HttpSession: org.apache.catalina.session.StandardSessionFacade@4f72f029. A new one will be created.
2018-10-14 18:10 [DEBUG] /user/register.do at position 2 of 14 in additional filter chain; firing Filter: 'ConcurrentSessionFilter'
2018-10-14 18:10 [DEBUG] /user/register.do at position 3 of 14 in additional filter chain; firing Filter: 'WebAsyncManagerIntegrationFilter'
2018-10-14 18:10 [DEBUG] /user/register.do at position 4 of 14 in additional filter chain; firing Filter: 'HeaderWriterFilter'
2018-10-14 18:10 [DEBUG] /user/register.do at position 5 of 14 in additional filter chain; firing Filter: 'CsrfFilter'
2018-10-14 18:10 [DEBUG] Invalid CSRF token found for http://localhost:8080/jedu/user/register.do


CSRF 토큰을 (쉽게) 포함시키기 위해 선행되어야 하는 작업으론..
  1. spring security taglib 선언
  2. ajax를 사용하는가?

in jsp....
<%@ taglib  uri="http://www.springframework.org/security/tags"  prefix="sec" %>

위 처럼 jsp에 taglib를 선언한다.



ajax를 사용하지 않고 form을 submit 하는 방식이라면 아래처럼..

<form class="m-0 sky-form" name="regForm" method="post" enctype="multipart/form-data">
    <sec:csrfInput />
    .........
</form>

만약 ajax를 사용한다면 <head> 태그 아래에...

<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<sec:csrfMetaTags />

CSRF 정보를 추가하는 방법이 각각 다르므로 유의해야 한다..

ajax 호출시에는 header 정보에 CSRF 정보를 넣어주어야 하는데 아래 코드와 같다.
var csrfParameter = $("meta[name='_csrf_parameter']").attr("content");
var csrfToken = $("meta[name='_csrf']").attr("content");
var csrfHeader = $("meta[name='_csrf_header']").attr("content");
var headers = {};
headers[csrfHeader] = csrfToken;

$.ajax({
    url: '<c:url value="/user/register.do" />',
    type: "POST",
    headers: headers,
    data: formData,
......
});



Spring - myBatis 환경에서 트랜잭션이 적용되지 않으면 아래 로그를 볼 수 있다.

Creating a new SqlSession
SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@1ad7d61e] was not registered for synchronization because synchronization is not active
Fetching JDBC Connection from DataSource
trace com.mchange.v2.resourcepool.BasicResourcePool@723b12e9 [managed: 20, unused: 19, excluded: 0] (e.g. com.mchange.v2.c3p0.impl.NewPooledConnection@6f25ab49)
JDBC Connection [com.mchange.v2.c3p0.impl.NewProxyConnection@5beadc25] will not be managed by Spring
==>  Preparing: UPDATE iot.user SET user_name = ?, nickname = ?, email = ?, is_admin = ?, update_date = NOW() WHERE seq = ?
==> Parameters: Test46(String), Test?46(String), 12@abc.com(String), 0(String), 346(Integer)
<==    Updates: 1
Closing non transactional SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@1ad7d61e]
Returning JDBC Connection to DataSource


-- applicationContext-beans.xml
<!-- Annotation 기반의 Component Scan 필터(service package 만 검색) -->
<context:component-scan base-package="com.iot.pf">
      <context:include-filter type="regex" expression="\.*\.service\.*" />
</context:component-scan>

<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
       <!-- DB에 접속 하기 위해서 설정 -->
      <property name="dataSource" ref="dataSource" />   
      
      <!-- myBatis 기본 설정 -->
      <property name="configLocation" value="classpath:mybatis-configuration.xml" />
      
      <!-- query 적힌 xml 위치 -->
      <property name="mapperLocations" value="classpath:sql/SQL.*.xml" />
      <!-- 트랜잭션 관리를 위한 것 -->
      <property name="transactionFactory">
           <bean class="org.mybatis.spring.transaction.SpringManagedTransactionFactory" />
      </property>
</bean>

<!-- DAO(interface) 위치를 basePackage로.. -->
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
      <property name="basePackage" value="com.iot.pf.dao" />
</bean>

<!-- 트랜잭션 관리를 위한 bean  -->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
      <property name="dataSource" ref="dataSource" />
</bean>

-- servlet.xml

<context:component-scan base-package="com.iot.pf" use-default-filters="false" >
      <context:include-filter type="annotation" expression="org.springframework.stereotype.Controller" />
      <context:include-filter type="annotation" expression="org.springframework.stereotype.Component" />
</context:component-scan>

<!-- File Upload -->
      <property name="maxUploadSize" value="50000000" />
</bean>

<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
      <property name="prefix" value="/WEB-INF/views/"/>
      <property name="suffix" value=".jsp"/>
</bean>

<mvc:resources mapping="/resources/**" location="/resources/" />

<mvc:annotation-driven />

<!-- transaction annotation -->
<tx:annotation-driven transaction-manager="transactionManager"/>

-- service file
@Service("userService")
@Transactional(rollbackFor = {Exception.class})
public class UserServiceImpl implements UserService{
 
}


트랜잭션이 적용되면 아래 로그를 볼 수 있다.
Creating new transaction with name [com.iot.pf.service.impl.UserServiceImpl.update]: PROPAGATION_REQUIRED,ISOLATION_DEFAULT; '',-com.iot.pf.exception.AnomalyException,-java.lang.Exception
trace com.mchange.v2.resourcepool.BasicResourcePool@63cfcf47 [managed: 20, unused: 19, excluded: 0] (e.g. com.mchange.v2.c3p0.impl.NewPooledConnection@3f24eec6)
Acquired Connection [com.mchange.v2.c3p0.impl.NewProxyConnection@19668759] for JDBC transaction
Switching JDBC Connection [com.mchange.v2.c3p0.impl.NewProxyConnection@19668759] to manual commit
Getting transaction for [com.iot.pf.service.impl.UserServiceImpl.update]
Creating a new SqlSession
Registering transaction synchronization for SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@5c640a11]
JDBC Connection [com.mchange.v2.c3p0.impl.NewProxyConnection@19668759] will be managed by Spring
==>  Preparing: UPDATE iot.user SET user_name = ?, nickname = ?, email = ?, is_admin = ?, update_date = NOW() WHERE seq = ?
==> Parameters: Test46(String), Test46(String), 123@abc.com(String), 0(String), 346(Integer)
<==    Updates: 1
Releasing transactional SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@5c640a11]
Completing transaction for [com.iot.pf.service.impl.UserServiceImpl.update] after exception: com.iot.pf.exception.AnomalyException: You have an anomaly result value after [INSERT/UPDATE/DELETE] query!!!!
Applying rules to determine whether transaction should rollback on com.iot.pf.exception.AnomalyException: You have an anomaly result value after [INSERT/UPDATE/DELETE] query!!!!
Winning rollback rule is: RollbackRuleAttribute with pattern [com.iot.pf.exception.AnomalyException]
Transaction synchronization deregistering SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@5c640a11]
Transaction synchronization closing SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@5c640a11]
Initiating transaction rollback
Rolling back JDBC transaction on Connection [com.mchange.v2.c3p0.impl.NewProxyConnection@19668759]
Releasing JDBC Connection [com.mchange.v2.c3p0.impl.NewProxyConnection@19668759] after transaction
Returning JDBC Connection to DataSource




java.lang.AbstractMethodError: Method com/mchange/v2/c3p0/impl/NewProxyPreparedStatement.isClosed()Z is abstract

<!-- https://mvnrepository.com/artifact/org.mybatis/mybatis -->
<dependency>
    <groupId>org.mybatis</groupId>
    <artifactId>mybatis</artifactId>
    <version>3.4.6</version>
</dependency>

<!-- https://mvnrepository.com/artifact/org.mybatis/mybatis-spring -->
<dependency>
    <groupId>org.mybatis</groupId>
    <artifactId>mybatis-spring</artifactId>
    <version>1.3.2</version>
</dependency>

위 처럼 mybatis 3.4.6, mybatis-spring 버전을 1.3.2로 사용하고 테스트를 하니 위 에러가 나더라..


<!-- https://mvnrepository.com/artifact/com.mchange/c3p0 -->
<dependency>
    <groupId>com.mchange</groupId>
    <artifactId>c3p0</artifactId>
    <version>0.9.5.2</version>
</dependency>

c3p0 라이브러리 버전이 낮아서 그런거 이므로 최신 버전으로 바꾸어 사용하면 된다.


Maven 빌드 중 아래 오류가 발생함.

[DEBUG] incrementalBuildHelper#beforeRebuildExecution

[INFO] Compiling 47 source files to C:\dev\workspace-class\spring-mybatis-grid\target\classes

[DEBUG] incrementalBuildHelper#afterRebuildExecution

[INFO] /C:/[경로]/java/com/edu/test/NamingReflection.java: C:\dev\workspace-class\spring-mybatis-grid\src\main\java\com\edu\test\NamingReflection.java uses unchecked or unsafe operations.

[INFO] /C:/[경로]/java/com/edu/test/NamingReflection.java: Recompile with -Xlint:unchecked for details.

[INFO] -------------------------------------------------------------

[ERROR] COMPILATION ERROR :

[INFO] -------------------------------------------------------------

[ERROR] /C:/[경로]/java/com/edu/test/DepartmentsServiceTest.java:[12,17] package org.junit does not exist

[ERROR] /C:/[경로]/java/com/edu/test/DepartmentsServiceTest.java:[13,24] package org.junit.runner does not exist

[ERROR] /C:/[경로]/java/com/edu/test/DepartmentsServiceTest.java:[14,40] package org.springframework.test.context does not exist

[ERROR] /C:/[경로]/java/com/edu/test/DepartmentsServiceTest.java:[15,47] package org.springframework.test.context.junit4 does not exist

[ERROR] /C:/[경로]/java/com/edu/test/DepartmentsServiceTest.java:[20,2] cannot find symbol symbol: class RunWith

[ERROR] /C:/[경로]/java/com/edu/test/DepartmentsServiceTest.java:[21,2] cannot find symbol symbol: class ContextConfiguration


오류나는 클래스를 살펴보니 모두 TEST 케이스에서만 에러난다.

패키지를 못 읽어 오는 것이기에 pom.xml 을 수정해야 한다.

<dependency> <groupId>org.springframework</groupId> <artifactId>spring-test</artifactId> <version>${spring.version}</version> <scope>test</scope> </dependency>

에서 scope 를 삭제한다. 아래처럼  

<dependency> <groupId>org.springframework</groupId> <artifactId>spring-test</artifactId> <version>${spring.version}</version> </dependency>


SLF4J: Failed to load class "org.slf4j.impl.StaticLoggerBinder".
SLF4J: Defaulting to no-operation (NOP) logger implementation


jxls 라이브러리를 사용하면성 기존에 있던 log4j와 jxls에서 사용하는 slf4j가 충돌아닌 충돌이 나더라...



로그에 나온 링크로 가보면 저렇게 써 있을 뿐 정확한 안내는 없다.
(실은 저게 정확할 수도..?)

라이브러리 로드 순서도 영향이 있을까 싶어 아래처럼 pom.xml 을 수정하니 정상적으로 로그가 출력된다.

		<!-- https://mvnrepository.com/artifact/org.slf4j/slf4j-api -->
		<dependency>
		    <groupId>org.slf4j</groupId>
		    <artifactId>slf4j-api</artifactId>
		    <version>1.7.25</version>
		</dependency>

		<!-- https://mvnrepository.com/artifact/org.slf4j/jcl-over-slf4j -->
		<dependency>
		    <groupId>org.slf4j</groupId>
		    <artifactId>jcl-over-slf4j</artifactId>
		    <version>1.7.25</version>
		</dependency>

		<!-- https://mvnrepository.com/artifact/org.slf4j/slf4j-log4j12 -->
		<dependency>
		    <groupId>org.slf4j</groupId>
		    <artifactId>slf4j-log4j12</artifactId>
		    <version>1.7.25</version>
		</dependency>

		<!-- https://mvnrepository.com/artifact/log4j/log4j -->
		<dependency>
		    <groupId>log4j</groupId>
		    <artifactId>log4j</artifactId>
		    <version>1.2.17</version>
		</dependency>


잘 된다... 


이런 에러가 발생 했을 경우.. 

to enable the nesting exclude '......./' from ~~~

 
pom.xml 에 아래 태그를 삭제한다.

<sourceDirectory>src</sourceDirectory> 

해결..

현재 작업하고 있는 프로젝트의 모든 프레임워크를 최신버전으로 세팅을 하고 서비스 클래스를 jUnit으로 작성해보았다...


Maven jUnit dependency
		
			junit
			junit
			4.7
			test
		


Maven Spring framework test
		
			org.springframework
			org.springframework.test
			3.2.3.RELEASE
		




AbstractApplicationContextTest.java
package com.openerp.service.test;

import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = {
		"file:src/resources/applicationContext-datasource.xml", 
		"file:src/resources/applicationContext-transaction.xml", 
		"file:src/resources/applicationContext.xml"
			})
public class AbstractApplicationContextTest {
	@Autowired ApplicationContext ctx;
}

여기서 ContextConfiguration의 locations이 문제가 많다.
일반적인 설정으로 dataSource, transaction, AOP 설정등 모든걸 하나의 xml에 설정을 한다면 하나만 쓰면 되지만, 위 처럼 각 기능별(또는 업무별)로 설정을 나누어 버리면 위 처럼 순서를 맞추어 주어야 한다.
그리고 applicationContext 파일의 위치가 조금 달라지면 classpath를 쓸때 생각을 많이 해야 한다.


ApplicationContext.xml
	
	
		
	

	
    
	  
	  
	  
	  
	  
			
	  
	
	
	
		 
	

어노테이션으로 bean을 자동으로 찾도록 했다.
jUnit 에서도 이 어노테이션을 가져올 수 있도록 지원하기 때문에 일일이 bean으로 서비스 클래스(테스트 대상)를 등록할 필요는 없다.

기타 설정은 패스...

UserServiceTest.java
package com.openerp.service.test;

import java.util.Calendar;
import java.util.List;
import javax.annotation.Resource;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.junit.Test;
import com.openerp.common.utils.JSONUtil;
import com.openerp.common.utils.Parameters;
import com.openerp.domain.User;
import com.openerp.service.UserService;

public class UserServiceTest extends AbstractApplicationContextTest{
	private static Log log = LogFactory.getLog(UserServiceTest.class);
	
	@Resource(name="userService")
	private UserService userService;
	    
	@Test
	public void testSelectAllUser(){
		// 아래 Parameters는 그냥 Map으로 보시면 됨..
		Parameters params = new Parameters();
		params.addValue("isDisable", "");
		List users = userService.listUsers(params);
		// 아래 코드는 JSON 형식으로 바꿔주는 로직.. 
		System.out.println(JSONUtil.toJSON(users, null));
	}
}

어노테이션으로 등록된 UserService를 가지고 오기 위해 @Resource 어노테이션을 이용했다.
applicationContext.xml에 bean 태그를 이용해서 등록한 bean은 @Autowired가 가능하나, 어노테이션 검색으로 찾은 bean은 안되더라.
@Resource를 이용해서 검색하게 하니 정상적으로 작동을 한다...
지금 이 클래스는 UserService에 대한 테스트 인데 다른 Service에 대한 테스트도 해야 한다면 지금과 같이 AbstractApplicationContextTest 만 상속해서 사용을 하면 된다.
이는 각 서비스가 사용하는 설정(applicationContext)가 동일할 경우이고, 만약 각 업무별로 applicationContext을 나누어 사용한다면 AbstractApplicationContextTest 외에 다른 클래스를 만들어 상속해서 쓰면 된다.
(결론은 설정을 안쪼갰다면 하나의 슈퍼클래스에 설정을 밀어넣고 상속해서 사용한다는 것..)


jUnit 등 Test Case를 적용할 경우 편하긴 편하다.
서버(tomcat 등)를 재시작 할 필요도 없고, 코드만 고치고 해당 @Test 어노테이션이 붙은 메서드를 jUnit으로 실행만 하면 기존 방법(서버를 통한)과 동일한 결과를 이루어 낼 수 있다.
java, xml 코드 하나 고치고 빌드 -> 서버 재시작 -> 웹에서 액션 -> 결과 확인 무한 반복...
이제 이런걸 안하고도 서비스 클래스를 쉽게 작성할 수 있으니 개발 효율성이 향상되는 효과를 누릴 수 있다.

 

Security 3.1.4를 사용하면서 LoginUrl 설정을 하면서 아래와 같은 워닝을 보았다.. 
	
		
		
	

확인해 본 결과 아래와 같이 변경 되었다..

org.springframework.security.web.authentication.LoginUrlAuthenticationEntryPoint.setLoginFormUrl(String) 
          use constructor injection 

그러므로 아래와 같이 변경해준다..
	
		
		
	


아래는 deprecated 된 항목을 살펴볼 수 있는 URL.

http://static.springsource.org/spring-security/site/docs/3.1.x/apidocs/deprecated-list.html 
java.lang.NoSuchMethodError: org.slf4j.spi.LocationAwareLogger.log(Lorg/slf4j/Marker;Ljava/lang/String;ILjava/lang/String;[Ljava/lang/Object;Ljava/lang/Throwable;)V

이런 에러가 나는데... SLF4J와 Spring Context 가 가지고 있는 slf4j 가 충돌이 나서 그런거므로 maven 설정을 아래와 같이..

 
		
			org.springframework
			org.springframework.spring-library
	    	libd
			3.2.3.RELEASE
			
		        
					javax.servlet
		            com.springsource.javax.servlet
				
		        
		        	org.springframework.context
		        	org.springframework
		        
		        
		        	
		        		org.springframework.context.support
		        	
		        	org.springframework
		        
			
		

		
			org.springframework
			spring-context
			3.2.3.RELEASE
			
                
                
                    commons-logging
                    commons-logging
                 
            
		
		
		
			org.springframework
			spring-context-support
			3.2.3.RELEASE
			
                
                
                    commons-logging
                    commons-logging
                 
            
		

+ Recent posts