'JAVA'에 해당되는 글 76건
- 2020.04.08 :: 프로그램 예외처리의 의미
- 2013.09.25 :: spring @Scheduled 중복 수행
- 2013.08.21 :: java zip 분할 압축
- 2013.05.20 :: Spring 3.1 TestCase (MockServletContext 사용)
- 2013.05.15 :: org.apache.poi.EncryptedDocumentException: Default password is invalid for docId/saltData/saltHash
- 2013.05.15 :: Error querying database. Cause: java.lang.NumberFormatException
- 2013.05.13 :: Could not get RequestDispatcher for jsp
- 2013.04.30 :: java.sql.SQLException: Already closed.
- 2013.04.20 :: cafe24 log4j 설정방법.
- 2013.04.07 :: 엑셀의 셀(Cell)번호를 java poi 형태로 변환............
- 2013.04.07 :: POI 엑셀 사용자 정의 서식 날짜일경우
- 2011.06.30 :: 프록시, 프록시 패턴, 데코레이터 패턴
- 2011.06.29 :: AOP(Aspect Oriented Programming) in Java
- 2011.04.23 :: 자바 쉘스크립트 실행.
- 2010.12.03 :: Log4J 설정방법 및 웹로직에서 사용방법
- 2010.07.16 :: Google Blogger API 사용
- 2010.05.15 :: javamail
- 2010.04.30 :: 배열을 복사하는 법(메소드)
- 2010.04.06 :: xml-rpc 를 이용한 티스토리(tistory) metaweblog api 자바(java)프로그래밍
- 2010.01.20 :: java 숫자 인지 아닌지 판별
- 2010.01.05 :: 문자열을 16진수 유니코드로 변경
- 2009.11.17 :: encodeURIComponent 로 인코딩 한 후 서버로 전송했을 때 한글이 깨지는 문제. 1
- 2009.10.28 :: OSGi 란 무엇인가?
- 2009.10.16 :: 공인인증서로 전자서명해보기 java
- 2009.10.06 :: java, 자바로 짠 배치 프로그램 ^^
- 2009.09.25 :: In-Container 방식 Struts Test(ServletTestCase)
- 2009.07.22 :: HTTPclient의 사용
- 2009.07.08 :: ibator -_- 주석 제거
- 2009.07.08 :: 용어
- 2009.07.04 :: sts
예외 처리
정상적인 흐름.
- ex) 커피빈 에서 따뜻한 아메리카노를 주문할때입니다.
1. 따뜻한 아메리카노 를 주문한다.
2. 결제를 한다.
3. 커피머신에서 커피를 내린다.
4. 내린 커피를 컵에 담는다.
5. 커피가 담긴 컵을 준다.
이렇게 정상적인 흐름입니다.
위의 흐름에서
3. 커피머신에 들어갈 콩이 없어서 예외(exception) 가 발생하였습니다.
이런 상황에서는 어떻게 해야 할지 판단을 해야 합니다.
1. 손님에게 커피콩이 없어서 환불해준다.
-> 환불처리로직
2. 다른 음료로 대체할지 물어본다.
-> 다른 음료로 대체하는 로직
3. 1시간 뒤에 커피 콩이 입고 되는데 그때까지 기다릴건지 물어본다.
-> 기다릴건지 물어보는 로직
cafe24 jsp 호스팅 받고 있다.
spring @Scheduled 중복 수행...
많이 찾아봤다...
톰캣 스타트 하면....
[ INFO] [2013-09-25 00:06:49,249][main] AbstractApplicationContext.java.prepareRefresh()-[495 Line] Refreshing WebApplicationContext for namespace 'appServlet-servlet': startup date [Wed Sep 25 00:06:49 KST 2013]; parent: Root WebApplicationContext
이렇게 두번 올라간다.(안올라가면 좋은건지 나쁜건지...모르겟다.
문제는 @Scheduled 이 두번 실행된다.
아주 약간의 시간차를 두고....
인터넷 찾아봐도 없다.
그래서....
우회?적인 방법으로 처리함.
DB 처리 방식.
CREATE TABLE `TBCRON01` (
`CLASSIFICATION` varchar(50) NOT NULL COMMENT '크론작업구분',
`JOBDATE` varchar(20) NOT NULL COMMENT '작업등록시간',
PRIMARY KEY (`CLASSIFICATION`,`JOBDATE`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8;
@Resource(name="sqlSession")
private SqlSession session;
HashMap<String, String> mp = new HashMap<String, String>();
mp.put("classification", "DatabaseBackupExecutor");
mp.put("jobdate", CommonUtil.getDateFormat("yyyy_MM_dd_HH_mm"));
session.update("SqlCronMapper.cronInsert", mp);
꼼수긴 해도...어쩔 도리가 없다....
하나의 파일로 압축하는건 많은데
파일들을 분할 압축하는거는 없다.
zip4j 오랫동안 찾다가 이제 찾음
소스 정말 심플함.
/*
* Copyright 2010 Srikanth Reddy Lingala
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package net.lingala.zip4j.examples.zip;
import java.io.File;
import java.util.ArrayList;
import net.lingala.zip4j.core.ZipFile;
import net.lingala.zip4j.exception.ZipException;
import net.lingala.zip4j.model.ZipParameters;
import net.lingala.zip4j.util.Zip4jConstants;
/**
* Demonstrated how to create a split zip file
*
* @author Srikanth Reddy Lingala
*
*/
public class CreateSplitZipFile {
public CreateSplitZipFile() {
try {
// Initiate ZipFile object with the path/name of the zip file.
ZipFile zipFile = new ZipFile("c:\\ZipTest\\CreateSplitZipFile.zip");
// Build the list of files to be added in the array list
// Objects of type File have to be added to the ArrayList
ArrayList filesToAdd = new ArrayList();//압출할 파일 대상들.
filesToAdd.add(new File("c:\\ZipTest\\sample.txt"));
filesToAdd.add(new File("c:\\ZipTest\\myvideo.avi"));
filesToAdd.add(new File("c:\\ZipTest\\mysong.mp3"));
// Initiate Zip Parameters which define various properties such
// as compression method, etc.
ZipParameters parameters = new ZipParameters();
// set compression method to store compression
parameters.setCompressionMethod(Zip4jConstants.COMP_DEFLATE);
// Set the compression level. This value has to be in between 0 to 9
parameters.setCompressionLevel(Zip4jConstants.DEFLATE_LEVEL_NORMAL);
// Create a split file by setting splitArchive parameter to true
// and specifying the splitLength. SplitLenth has to be greater than
// 65536 bytes
// Please note: If the zip file already exists, then this method throws an
// exception
zipFile.createZipFile(filesToAdd, parameters, true, 10485760);//파일 사이즈 byte단위
} catch (ZipException e) {
e.printStackTrace();
}
}
/**
* @param args
*/
public static void main(String[] args) {
new CreateSplitZipFile();
}
}
svn 다운로드 주소 : https://faz.googlecode.com/svn/trunk/springtestcase3.1
좀 틀린부분도 있습니다. 너그러이 용서를....
package kr.faz.testcase;
import javax.inject.Inject;
import kr.faz.util.StringUtils;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.mock.web.MockHttpServletRequest;
import org.springframework.mock.web.MockHttpServletResponse;
import org.springframework.mock.web.MockServletContext;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter;
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping;
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = "classpath:kr/faz/test-servlet-context.xml" )
@Transactional
public class MockControllerTest {
@Inject
private RequestMappingHandlerAdapter handlerAdapter;
@Inject
private RequestMappingHandlerMapping handlerMapping;
MockHttpServletRequest request ;
MockHttpServletResponse response;
@Before
public void setUp() {
request = new MockHttpServletRequest();
response = new MockHttpServletResponse();
}
@Test //주문서로 이관
public void urlTest() throws Exception {
setRequest("/test.do?companycode=2&infolidx=2&filelidx=2&orderdate=20130413", "POST");
}
public void setRequest(String uri, String method) throws Exception {
int questionIndex = uri.indexOf("?");
String url = null;
String param = null;
if ( questionIndex != -1 ){
url = uri.substring(0, questionIndex);
param = uri.substring(questionIndex+1);
} else {
url = uri;
}
request.setRequestURI(url);
if ( StringUtils.isNotEmpty(param)) {
String params[] = param.split("&");
for ( String value : params) {
//파라미터 값이 있으면 셋팅
String[] v = value.split("=");
if ( v.length == 2) {
request.addParameter(v[0], v[1]);
}
}
}
System.out.println("URI : "+ request.getRequestURI());
System.out.println("URL : "+ request.getRequestURL());
System.out.println("QueryString : "+ request.getQueryString());
request.setMethod(method);
Object handler = handlerMapping.getHandler(request).getHandler();
handlerAdapter.handle(request, response, handler);
System.out.println("response data : "+ response.getContentAsString());
request.close();
}
}
org.apache.poi.EncryptedDocumentException: Default password is invalid for docId/saltData/saltHash
at org.apache.poi.hssf.record.RecordFactoryInputStream$StreamEncryptionInfo.createDecryptingStream(RecordFactoryInputStream.java:116)
at org.apache.poi.hssf.record.RecordFactoryInputStream.<init>(RecordFactoryInputStream.java:184)
at org.apache.poi.hssf.record.RecordFactory.createRecords(RecordFactory.java:440)
at org.apache.poi.hssf.usermodel.HSSFWorkbook.<init>(HSSFWorkbook.java:280)
at org.apache.poi.hssf.usermodel.HSSFWorkbook.<init>(HSSFWorkbook.java:243)
at org.apache.poi.hssf.usermodel.HSSFWorkbook.<init>(HSSFWorkbook.java:187)
at org.apache.poi.hssf.usermodel.HSSFWorkbook.<init>(HSSFWorkbook.java:169)
at kr.faz.excelupload.controller.ExceluploadController.parsingTypeByExclude(ExceluploadController.java:496)
at kr.faz.excelupload.controller.ExceluploadController.adminExceluploadInsertFormAction(ExceluploadController.java:115)
at kr.faz.excelupload.controller.ExceluploadController$$FastClassByCGLIB$$7002440e.invoke(<generated>)
at net.sf.cglib.proxy.MethodProxy.invoke(MethodProxy.java:204)
at org.springframework.aop.framework.Cglib2AopProxy$CglibMethodInvocation.invokeJoinpoint(Cglib2AopProxy.java:689)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:150)
at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:110)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:172)
at org.springframework.aop.aspectj.MethodInvocationProceedingJoinPoint.proceed(MethodInvocationProceedingJoinPoint.java:80)
at kr.faz.aop.SessionCheck.adminSessionCheck(SessionCheck.java:57)
at sun.reflect.GeneratedMethodAccessor102.invoke(Unknown Source)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
at java.lang.reflect.Method.invoke(Method.java:597)
at org.springframework.aop.aspectj.AbstractAspectJAdvice.invokeAdviceMethodWithGivenArgs(AbstractAspectJAdvice.java:621)
at org.springframework.aop.aspectj.AbstractAspectJAdvice.invokeAdviceMethod(AbstractAspectJAdvice.java:610)
at org.springframework.aop.aspectj.AspectJAroundAdvice.invoke(AspectJAroundAdvice.java:65)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:172)
at org.springframework.aop.interceptor.ExposeInvocationInterceptor.invoke(ExposeInvocationInterceptor.java:90)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:172)
at org.springframework.aop.framework.Cglib2AopProxy$DynamicAdvisedInterceptor.intercept(Cglib2AopProxy.java:622)
at kr.faz.excelupload.controller.ExceluploadController$$EnhancerByCGLIB$$ee8dc86a.adminExceluploadInsertFormAction(<generated>)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
at java.lang.reflect.Method.invoke(Method.java:597)
at org.springframework.web.method.support.InvocableHandlerMethod.invoke(InvocableHandlerMethod.java:213)
at org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:126)
at org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:96)
at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandlerMethod(RequestMappingHandlerAdapter.java:617)
at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:578)
at org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:80)
at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:923)
at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:852)
at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:882)
at org.springframework.web.servlet.FrameworkServlet.doPost(FrameworkServlet.java:789)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:637)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:717)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:290)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206)
at org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:88)
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:76)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:235)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206)
at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:233)
at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:191)
at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:127)
at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:102)
at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:109)
at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:293)
at org.apache.jk.server.JkCoyoteHandler.invoke(JkCoyoteHandler.java:190)
at org.apache.jk.common.HandlerRequest.invoke(HandlerRequest.java:291)
at org.apache.jk.common.Socket.invoke(Socket.java:776)
at org.apache.jk.common.Socket.processConnection(Socket.java:705)
at org.apache.jk.common.Socket$SocketConnection.runIt(Socket.java:898)
at org.apache.tomcat.util.threads.ThreadPool$ControlRunnable.run(ThreadPool.java:690)
at java.lang.Thread.run(Thread.java:619)
Mybatis 에서 나는 오류
<if test="section != null && section != '' && section != 'X' " >
->
<if test='section != null && section != "" && section != "X" ' >
변경하면 된다.
org.mybatis.spring.MyBatisSystemException: nested exception is org.apache.ibatis.exceptions.PersistenceException:
### Error querying database. Cause: java.lang.NumberFormatException: For input string: "X"
### Cause: java.lang.NumberFormatException: For input string: "X"
at org.mybatis.spring.MyBatisExceptionTranslator.translateExceptionIfPossible(MyBatisExceptionTranslator.java:73)
at org.mybatis.spring.SqlSessionTemplate$SqlSessionInterceptor.invoke(SqlSessionTemplate.java:364)
at $Proxy12.selectList(Unknown Source)
at org.mybatis.spring.SqlSessionTemplate.selectList(SqlSessionTemplate.java:194)
at kr.faz.goodsinout.dao.impl.GoodsinoutDaoImpl.list(GoodsinoutDaoImpl.java:26)
at kr.faz.goodsinout.service.impl.GoodsinoutServiceImpl.list(GoodsinoutServiceImpl.java:26)
at kr.faz.goodsinout.controller.GoodsinoutController.adminGoodsinoutList(GoodsinoutController.java:70)
at kr.faz.goodsinout.controller.GoodsinoutController$$FastClassByCGLIB$$b9c3d7bc.invoke(<generated>)
at net.sf.cglib.proxy.MethodProxy.invoke(MethodProxy.java:204)
at org.springframework.aop.framework.Cglib2AopProxy$CglibMethodInvocation.invokeJoinpoint(Cglib2AopProxy.java:689)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:150)
at org.springframework.aop.aspectj.MethodInvocationProceedingJoinPoint.proceed(MethodInvocationProceedingJoinPoint.java:80)
at kr.faz.aop.SessionCheck.adminSessionCheck(SessionCheck.java:58)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
at java.lang.reflect.Method.invoke(Method.java:597)
at org.springframework.aop.aspectj.AbstractAspectJAdvice.invokeAdviceMethodWithGivenArgs(AbstractAspectJAdvice.java:621)
at org.springframework.aop.aspectj.AbstractAspectJAdvice.invokeAdviceMethod(AbstractAspectJAdvice.java:610)
at org.springframework.aop.aspectj.AspectJAroundAdvice.invoke(AspectJAroundAdvice.java:65)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:172)
at org.springframework.aop.interceptor.ExposeInvocationInterceptor.invoke(ExposeInvocationInterceptor.java:90)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:172)
at org.springframework.aop.framework.Cglib2AopProxy$DynamicAdvisedInterceptor.intercept(Cglib2AopProxy.java:622)
at kr.faz.goodsinout.controller.GoodsinoutController$$EnhancerByCGLIB$$a5f0006b.adminGoodsinoutList(<generated>)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
at java.lang.reflect.Method.invoke(Method.java:597)
at org.springframework.web.method.support.InvocableHandlerMethod.invoke(InvocableHandlerMethod.java:213)
at org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:126)
at org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:96)
at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandlerMethod(RequestMappingHandlerAdapter.java:617)
at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:578)
at org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:80)
at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:923)
at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:852)
at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:882)
at org.springframework.web.servlet.FrameworkServlet.doPost(FrameworkServlet.java:789)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:710)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:803)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:290)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206)
at org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:88)
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:76)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:235)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206)
at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:233)
at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:175)
at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:128)
at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:102)
at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:109)
at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:263)
at org.apache.coyote.http11.Http11AprProcessor.process(Http11AprProcessor.java:852)
at org.apache.coyote.http11.Http11AprProtocol$Http11ConnectionHandler.process(Http11AprProtocol.java:584)
at org.apache.tomcat.util.net.AprEndpoint$Worker.run(AprEndpoint.java:1508)
at java.lang.Thread.run(Thread.java:619)
Caused by: org.apache.ibatis.exceptions.PersistenceException:
### Error querying database. Cause: java.lang.NumberFormatException: For input string: "X"
### Cause: java.lang.NumberFormatException: For input string: "X"
at org.apache.ibatis.exceptions.ExceptionFactory.wrapException(ExceptionFactory.java:23)
at org.apache.ibatis.session.defaults.DefaultSqlSession.selectList(DefaultSqlSession.java:107)
at org.apache.ibatis.session.defaults.DefaultSqlSession.selectList(DefaultSqlSession.java:98)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
at java.lang.reflect.Method.invoke(Method.java:597)
at org.mybatis.spring.SqlSessionTemplate$SqlSessionInterceptor.invoke(SqlSessionTemplate.java:354)
... 55 more
Caused by: java.lang.NumberFormatException: For input string: "X"
at sun.misc.FloatingDecimal.readJavaFormatString(FloatingDecimal.java:1224)
at java.lang.Double.parseDouble(Double.java:510)
at org.apache.ibatis.ognl.OgnlOps.doubleValue(OgnlOps.java:259)
at org.apache.ibatis.ognl.OgnlOps.compareWithConversion(OgnlOps.java:143)
at org.apache.ibatis.ognl.OgnlOps.isEqual(OgnlOps.java:186)
at org.apache.ibatis.ognl.OgnlOps.equal(OgnlOps.java:578)
at org.apache.ibatis.ognl.ASTNotEq.getValueBody(ASTNotEq.java:51)
at org.apache.ibatis.ognl.SimpleNode.evaluateGetValueBody(SimpleNode.java:170)
at org.apache.ibatis.ognl.SimpleNode.getValue(SimpleNode.java:210)
at org.apache.ibatis.ognl.ASTAnd.getValueBody(ASTAnd.java:56)
at org.apache.ibatis.ognl.SimpleNode.evaluateGetValueBody(SimpleNode.java:170)
at org.apache.ibatis.ognl.SimpleNode.getValue(SimpleNode.java:210)
at org.apache.ibatis.ognl.Ognl.getValue(Ognl.java:333)
at org.apache.ibatis.ognl.Ognl.getValue(Ognl.java:413)
at org.apache.ibatis.ognl.Ognl.getValue(Ognl.java:395)
at org.apache.ibatis.scripting.xmltags.OgnlCache.getValue(OgnlCache.java:45)
at org.apache.ibatis.scripting.xmltags.ExpressionEvaluator.evaluateBoolean(ExpressionEvaluator.java:29)
at org.apache.ibatis.scripting.xmltags.IfSqlNode.apply(IfSqlNode.java:30)
at org.apache.ibatis.scripting.xmltags.MixedSqlNode.apply(MixedSqlNode.java:29)
at org.apache.ibatis.scripting.xmltags.IfSqlNode.apply(IfSqlNode.java:31)
at org.apache.ibatis.scripting.xmltags.MixedSqlNode.apply(MixedSqlNode.java:29)
at org.apache.ibatis.scripting.xmltags.TrimSqlNode.apply(TrimSqlNode.java:47)
at org.apache.ibatis.scripting.xmltags.MixedSqlNode.apply(MixedSqlNode.java:29)
at org.apache.ibatis.scripting.xmltags.DynamicSqlSource.getBoundSql(DynamicSqlSource.java:37)
at org.apache.ibatis.mapping.MappedStatement.getBoundSql(MappedStatement.java:265)
at org.apache.ibatis.executor.CachingExecutor.query(CachingExecutor.java:79)
at org.apache.ibatis.session.defaults.DefaultSqlSession.selectList(DefaultSqlSession.java:104)
... 61 more
ERROR: org.springframework.web.servlet.tags.form.HiddenInputTag - Neither BindingResult nor plain target object for bean name 'm' available as request attribute
java.lang.IllegalStateException: Neither BindingResult nor plain target object for bean name 'm' available as request attribute
at org.springframework.web.servlet.support.BindStatus.<init>(BindStatus.java:141)
at org.springframework.web.servlet.tags.form.AbstractDataBoundFormElementTag.getBindStatus(AbstractDataBoundFormElementTag.java:178)
at org.springframework.web.servlet.tags.form.AbstractDataBoundFormElementTag.getPropertyPath(AbstractDataBoundFormElementTag.java:198)
at org.springframework.web.servlet.tags.form.AbstractDataBoundFormElementTag.getName(AbstractDataBoundFormElementTag.java:164)
at org.springframework.web.servlet.tags.form.AbstractDataBoundFormElementTag.autogenerateId(AbstractDataBoundFormElementTag.java:151)
at org.springframework.web.servlet.tags.form.AbstractDataBoundFormElementTag.resolveId(AbstractDataBoundFormElementTag.java:142)
at org.springframework.web.servlet.tags.form.AbstractDataBoundFormElementTag.writeDefaultAttributes(AbstractDataBoundFormElementTag.java:126)
at org.springframework.web.servlet.tags.form.AbstractHtmlElementTag.writeDefaultAttributes(AbstractHtmlElementTag.java:421)
at org.springframework.web.servlet.tags.form.HiddenInputTag.writeTagContent(HiddenInputTag.java:77)
at org.springframework.web.servlet.tags.form.AbstractFormTag.doStartTagInternal(AbstractFormTag.java:102)
at org.springframework.web.servlet.tags.RequestContextAwareTag.doStartTag(RequestContextAwareTag.java:79)
at org.apache.jsp.WEB_002dINF.views.admin.goodsinout.goodsinoutList_jsp._jspx_meth_form_005fhidden_005f0(goodsinoutList_jsp.java:313)
at org.apache.jsp.WEB_002dINF.views.admin.goodsinout.goodsinoutList_jsp._jspService(goodsinoutList_jsp.java:212)
at org.apache.jasper.runtime.HttpJspBase.service(HttpJspBase.java:70)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:803)
at org.apache.jasper.servlet.JspServletWrapper.service(JspServletWrapper.java:393)
at org.apache.jasper.servlet.JspServlet.serviceJspFile(JspServlet.java:320)
at org.apache.jasper.servlet.JspServlet.service(JspServlet.java:266)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:803)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:290)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206)
at org.apache.catalina.core.ApplicationDispatcher.invoke(ApplicationDispatcher.java:654)
at org.apache.catalina.core.ApplicationDispatcher.processRequest(ApplicationDispatcher.java:445)
at org.apache.catalina.core.ApplicationDispatcher.doForward(ApplicationDispatcher.java:379)
at org.apache.catalina.core.ApplicationDispatcher.forward(ApplicationDispatcher.java:292)
at org.springframework.web.servlet.view.InternalResourceView.renderMergedOutputModel(InternalResourceView.java:238)
at org.springframework.web.servlet.view.AbstractView.render(AbstractView.java:262)
at org.springframework.web.servlet.DispatcherServlet.render(DispatcherServlet.java:1180)
at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:950)
at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:852)
at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:882)
at org.springframework.web.servlet.FrameworkServlet.doPost(FrameworkServlet.java:789)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:710)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:803)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:290)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206)
at org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:88)
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:76)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:235)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206)
at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:233)
at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:175)
at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:128)
at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:102)
at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:109)
at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:263)
at org.apache.coyote.http11.Http11AprProcessor.process(Http11AprProcessor.java:852)
at org.apache.coyote.http11.Http11AprProtocol$Http11ConnectionHandler.process(Http11AprProtocol.java:584)
at org.apache.tomcat.util.net.AprEndpoint$Worker.run(AprEndpoint.java:1508)
at java.lang.Thread.run(Thread.java:619)
jsp파일이 인식이 안된다.
tomcat/conf 의 web.xml 이 존재하는지 먼저 살펴봐야 한다.
없으면 복시해 넣는다.
ype Exception report
message
description The server encountered an internal error () that prevented it from fulfilling this request.
exception
javax.servlet.ServletException: Could not get RequestDispatcher for [/WEB-INF/views/login.jsp]: Check that the corresponding file exists within your web application archive! org.springframework.web.servlet.view.InternalResourceView.renderMergedOutputModel(InternalResourceView.java:219) org.springframework.web.servlet.view.AbstractView.render(AbstractView.java:262) org.springframework.web.servlet.DispatcherServlet.render(DispatcherServlet.java:1180) org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:950) org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:852) org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:882) org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:778) javax.servlet.http.HttpServlet.service(HttpServlet.java:617) javax.servlet.http.HttpServlet.service(HttpServlet.java:717) org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:88) org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:76)
note The full stack trace of the root cause is available in the Apache Tomcat/6.0.35 logs.
error: java.sql.SQLException: Already closed. oracle
상황 : 이상없이 잘 사용하던 커넥션이 한번씩 끊긴다. 이후 재사용시에는 무리없는 활동을 보여준다.
이유 : 데이터베이스 입장에서 오랫동안 휴면되는 커넥션은 불필요하다고 판단하고, 끊어버린다. 때문에 커넥션풀 설정시 연결 확인용 쿼리를 지정해주어야 한다.
<property name="driverClassName" value="${db.driverName}"></property>
<property name="url" value="${db.url}"></property>
<property name="username" value="${db.username}"></property>
<property name="password" value="${db.password}"></property>
<property name="initialSize" value="${db.initialSize}"></property>
<property name="maxActive" value="${db.maxActive}"></property>
<property name="validationQuery" value="select 1 from dual" /> <—Oracle
<property name="validationQuery" value="select 1" /> <-- MySql -->
DEBUG: org.springframework.jdbc.datasource.DataSourceUtils - Could not close JDBC Connection
java.sql.SQLException: Already closed.
at org.apache.commons.dbcp.PoolableConnection.close(PoolableConnection.java:114)
at org.apache.commons.dbcp.PoolingDataSource$PoolGuardConnectionWrapper.close(PoolingDataSource.java:191)
at org.springframework.jdbc.datasource.DataSourceUtils.doReleaseConnection(DataSourceUtils.java:333)
at org.springframework.jdbc.datasource.DataSourceUtils.releaseConnection(DataSourceUtils.java:294)
at org.mybatis.spring.transaction.SpringManagedTransaction.close(SpringManagedTransaction.java:122)
at org.apache.ibatis.executor.BaseExecutor.close(BaseExecutor.java:78)
at org.apache.ibatis.executor.CachingExecutor.close(CachingExecutor.java:65)
at org.apache.ibatis.session.defaults.DefaultSqlSession.close(DefaultSqlSession.java:206)
at org.mybatis.spring.SqlSessionUtils.closeSqlSession(SqlSessionUtils.java:172)
at org.mybatis.spring.SqlSessionTemplate$SqlSessionInterceptor.invoke(SqlSessionTemplate.java:371)
at $Proxy15.selectList(Unknown Source)
at org.mybatis.spring.SqlSessionTemplate.selectList(SqlSessionTemplate.java:194)
at kr.faz.ordersheet.dao.impl.OrdersheetDaoImpl.list(OrdersheetDaoImpl.java:26)
at kr.faz.ordersheet.service.impl.OrdersheetServiceImpl.list(OrdersheetServiceImpl.java:27)
at kr.faz.ordersheet.controller.OrdersheetController.checkData(OrdersheetController.java:110)
at kr.faz.ordersheet.controller.OrdersheetController.adminOrdersheetList(OrdersheetController.java:74)
at kr.faz.ordersheet.controller.OrdersheetController$$FastClassByCGLIB$$86da2af8.invoke(<generated>)
at net.sf.cglib.proxy.MethodProxy.invoke(MethodProxy.java:204)
at org.springframework.aop.framework.Cglib2AopProxy$CglibMethodInvocation.invokeJoinpoint(Cglib2AopProxy.java:689)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:150)
at org.springframework.aop.aspectj.MethodInvocationProceedingJoinPoint.proceed(MethodInvocationProceedingJoinPoint.java:80)
at kr.faz.aop.SessionCheck.adminSessionCheck(SessionCheck.java:51)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
at java.lang.reflect.Method.invoke(Method.java:597)
at org.springframework.aop.aspectj.AbstractAspectJAdvice.invokeAdviceMethodWithGivenArgs(AbstractAspectJAdvice.java:621)
at org.springframework.aop.aspectj.AbstractAspectJAdvice.invokeAdviceMethod(AbstractAspectJAdvice.java:610)
at org.springframework.aop.aspectj.AspectJAroundAdvice.invoke(AspectJAroundAdvice.java:65)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:172)
at org.springframework.aop.interceptor.ExposeInvocationInterceptor.invoke(ExposeInvocationInterceptor.java:90)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:172)
at org.springframework.aop.framework.Cglib2AopProxy$DynamicAdvisedInterceptor.intercept(Cglib2AopProxy.java:622)
at kr.faz.ordersheet.controller.OrdersheetController$$EnhancerByCGLIB$$8d2c0c1.adminOrdersheetList(<generated>)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
at java.lang.reflect.Method.invoke(Method.java:597)
at org.springframework.web.method.support.InvocableHandlerMethod.invoke(InvocableHandlerMethod.java:213)
at org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:126)
at org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:96)
at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandlerMethod(RequestMappingHandlerAdapter.java:617)
at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:578)
at org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:80)
at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:923)
at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:852)
at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:882)
at org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:778)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:690)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:803)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:290)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206)
at org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:88)
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:76)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:235)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206)
at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:233)
at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:175)
at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:128)
at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:102)
at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:109)
at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:263)
at org.apache.coyote.http11.Http11AprProcessor.process(Http11AprProcessor.java:852)
at org.apache.coyote.http11.Http11AprProtocol$Http11ConnectionHandler.process(Http11AprProtocol.java:584)
at org.apache.tomcat.util.net.AprEndpoint$Worker.run(AprEndpoint.java:1508)
at java.lang.Thread.run(Thread.java:619)
org.springframework.dao.DataAccessResourceFailureException:
### Error querying database. Cause: com.mysql.jdbc.exceptions.jdbc4.MySQLNonTransientConnectionException: No operations allowed after connection closed.
### The error may exist in file [P:\WSC\sts\.metadata\.plugins\org.eclipse.wst.server.core\tmp0\wtpwebapps\cs\WEB-INF\classes\mybatis\xml\ordersheet.xml]
### The error may involve SqlOrdersheetMapper.ordersheetList
### The error occurred while executing a query
### SQL: SELECT ************** LIMIT ? , ?
### Cause: com.mysql.jdbc.exceptions.jdbc4.MySQLNonTransientConnectionException: No operations allowed after connection closed.
; SQL []; No operations allowed after connection closed.; nested exception is com.mysql.jdbc.exceptions.jdbc4.MySQLNonTransientConnectionException: No operations allowed after connection closed.
at org.springframework.jdbc.support.SQLExceptionSubclassTranslator.doTranslate(SQLExceptionSubclassTranslator.java:91)
at org.springframework.jdbc.support.AbstractFallbackSQLExceptionTranslator.translate(AbstractFallbackSQLExceptionTranslator.java:72)
at org.springframework.jdbc.support.AbstractFallbackSQLExceptionTranslator.translate(AbstractFallbackSQLExceptionTranslator.java:80)
at org.mybatis.spring.MyBatisExceptionTranslator.translateExceptionIfPossible(MyBatisExceptionTranslator.java:71)
at org.mybatis.spring.SqlSessionTemplate$SqlSessionInterceptor.invoke(SqlSessionTemplate.java:364)
at $Proxy15.selectList(Unknown Source)
at org.mybatis.spring.SqlSessionTemplate.selectList(SqlSessionTemplate.java:194)
at kr.faz.ordersheet.dao.impl.OrdersheetDaoImpl.list(OrdersheetDaoImpl.java:26)
at kr.faz.ordersheet.service.impl.OrdersheetServiceImpl.list(OrdersheetServiceImpl.java:27)
at kr.faz.ordersheet.controller.OrdersheetController.checkData(OrdersheetController.java:110)
at kr.faz.ordersheet.controller.OrdersheetController.adminOrdersheetList(OrdersheetController.java:74)
at kr.faz.ordersheet.controller.OrdersheetController$$FastClassByCGLIB$$86da2af8.invoke(<generated>)
at net.sf.cglib.proxy.MethodProxy.invoke(MethodProxy.java:204)
at org.springframework.aop.framework.Cglib2AopProxy$CglibMethodInvocation.invokeJoinpoint(Cglib2AopProxy.java:689)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:150)
at org.springframework.aop.aspectj.MethodInvocationProceedingJoinPoint.proceed(MethodInvocationProceedingJoinPoint.java:80)
at kr.faz.aop.SessionCheck.adminSessionCheck(SessionCheck.java:51)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
at java.lang.reflect.Method.invoke(Method.java:597)
at org.springframework.aop.aspectj.AbstractAspectJAdvice.invokeAdviceMethodWithGivenArgs(AbstractAspectJAdvice.java:621)
at org.springframework.aop.aspectj.AbstractAspectJAdvice.invokeAdviceMethod(AbstractAspectJAdvice.java:610)
at org.springframework.aop.aspectj.AspectJAroundAdvice.invoke(AspectJAroundAdvice.java:65)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:172)
at org.springframework.aop.interceptor.ExposeInvocationInterceptor.invoke(ExposeInvocationInterceptor.java:90)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:172)
at org.springframework.aop.framework.Cglib2AopProxy$DynamicAdvisedInterceptor.intercept(Cglib2AopProxy.java:622)
at kr.faz.ordersheet.controller.OrdersheetController$$EnhancerByCGLIB$$8d2c0c1.adminOrdersheetList(<generated>)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
at java.lang.reflect.Method.invoke(Method.java:597)
at org.springframework.web.method.support.InvocableHandlerMethod.invoke(InvocableHandlerMethod.java:213)
at org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:126)
at org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:96)
at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandlerMethod(RequestMappingHandlerAdapter.java:617)
at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:578)
at org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:80)
at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:923)
at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:852)
at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:882)
at org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:778)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:690)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:803)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:290)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206)
at org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:88)
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:76)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:235)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206)
at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:233)
at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:175)
at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:128)
at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:102)
at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:109)
at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:263)
at org.apache.coyote.http11.Http11AprProcessor.process(Http11AprProcessor.java:852)
at org.apache.coyote.http11.Http11AprProtocol$Http11ConnectionHandler.process(Http11AprProtocol.java:584)
at org.apache.tomcat.util.net.AprEndpoint$Worker.run(AprEndpoint.java:1508)
at java.lang.Thread.run(Thread.java:619)
Caused by: com.mysql.jdbc.exceptions.jdbc4.MySQLNonTransientConnectionException: No operations allowed after connection closed.
at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:39)
at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:27)
at java.lang.reflect.Constructor.newInstance(Constructor.java:513)
at com.mysql.jdbc.Util.handleNewInstance(Util.java:411)
at com.mysql.jdbc.Util.getInstance(Util.java:386)
at com.mysql.jdbc.SQLError.createSQLException(SQLError.java:1015)
at com.mysql.jdbc.SQLError.createSQLException(SQLError.java:989)
at com.mysql.jdbc.SQLError.createSQLException(SQLError.java:975)
at com.mysql.jdbc.SQLError.createSQLException(SQLError.java:920)
at com.mysql.jdbc.ConnectionImpl.throwConnectionClosedException(ConnectionImpl.java:1304)
at com.mysql.jdbc.ConnectionImpl.checkClosed(ConnectionImpl.java:1296)
at com.mysql.jdbc.ConnectionImpl.prepareStatement(ConnectionImpl.java:4511)
at com.mysql.jdbc.ConnectionImpl.prepareStatement(ConnectionImpl.java:4476)
at org.apache.commons.dbcp.DelegatingConnection.prepareStatement(DelegatingConnection.java:281)
at org.apache.commons.dbcp.PoolingDataSource$PoolGuardConnectionWrapper.prepareStatement(PoolingDataSource.java:313)
at sun.reflect.GeneratedMethodAccessor31.invoke(Unknown Source)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
at java.lang.reflect.Method.invoke(Method.java:597)
at org.apache.ibatis.logging.jdbc.ConnectionLogger.invoke(ConnectionLogger.java:52)
at $Proxy16.prepareStatement(Unknown Source)
at org.apache.ibatis.executor.statement.PreparedStatementHandler.instantiateStatement(PreparedStatementHandler.java:72)
at org.apache.ibatis.executor.statement.BaseStatementHandler.prepare(BaseStatementHandler.java:82)
at org.apache.ibatis.executor.statement.RoutingStatementHandler.prepare(RoutingStatementHandler.java:54)
at org.apache.ibatis.executor.SimpleExecutor.prepareStatement(SimpleExecutor.java:70)
at org.apache.ibatis.executor.SimpleExecutor.doQuery(SimpleExecutor.java:56)
at org.apache.ibatis.executor.BaseExecutor.queryFromDatabase(BaseExecutor.java:259)
at org.apache.ibatis.executor.BaseExecutor.query(BaseExecutor.java:132)
at org.apache.ibatis.executor.CachingExecutor.query(CachingExecutor.java:105)
at org.apache.ibatis.executor.CachingExecutor.query(CachingExecutor.java:81)
at org.apache.ibatis.session.defaults.DefaultSqlSession.selectList(DefaultSqlSession.java:104)
at org.apache.ibatis.session.defaults.DefaultSqlSession.selectList(DefaultSqlSession.java:98)
at sun.reflect.GeneratedMethodAccessor125.invoke(Unknown Source)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
at java.lang.reflect.Method.invoke(Method.java:597)
at org.mybatis.spring.SqlSessionTemplate$SqlSessionInterceptor.invoke(SqlSessionTemplate.java:354)
... 56 more
Caused by: com.mysql.jdbc.exceptions.jdbc4.CommunicationsException: Communications link failure
The last packet successfully received from the server was 101,739 milliseconds ago. The last packet sent successfully to the server was 101,739 milliseconds ago.
at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:39)
at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:27)
at java.lang.reflect.Constructor.newInstance(Constructor.java:513)
at com.mysql.jdbc.Util.handleNewInstance(Util.java:411)
at com.mysql.jdbc.SQLError.createCommunicationsException(SQLError.java:1121)
at com.mysql.jdbc.MysqlIO.send(MysqlIO.java:3871)
at com.mysql.jdbc.MysqlIO.sendCommand(MysqlIO.java:2484)
at com.mysql.jdbc.MysqlIO.sqlQueryDirect(MysqlIO.java:2664)
at com.mysql.jdbc.ConnectionImpl.execSQL(ConnectionImpl.java:2809)
at com.mysql.jdbc.ConnectionImpl.execSQL(ConnectionImpl.java:2758)
at com.mysql.jdbc.StatementImpl.executeQuery(StatementImpl.java:1612)
at com.mysql.jdbc.DatabaseMetaData.getUserName(DatabaseMetaData.java:6791)
at org.apache.commons.dbcp.DelegatingConnection.toString(DelegatingConnection.java:123)
at org.apache.commons.dbcp.PoolingDataSource$PoolGuardConnectionWrapper.toString(PoolingDataSource.java:355)
at java.lang.String.valueOf(String.java:2827)
at java.lang.StringBuilder.append(StringBuilder.java:115)
at org.mybatis.spring.transaction.SpringManagedTransaction.openConnection(SpringManagedTransaction.java:85)
at org.mybatis.spring.transaction.SpringManagedTransaction.getConnection(SpringManagedTransaction.java:66)
at org.apache.ibatis.executor.BaseExecutor.getConnection(BaseExecutor.java:271)
at org.apache.ibatis.executor.SimpleExecutor.prepareStatement(SimpleExecutor.java:69)
... 67 more
Caused by: java.net.SocketException: Connection reset by peer: socket write error
at java.net.SocketOutputStream.socketWrite0(Native Method)
at java.net.SocketOutputStream.socketWrite(SocketOutputStream.java:92)
at java.net.SocketOutputStream.write(SocketOutputStream.java:136)
at java.io.BufferedOutputStream.flushBuffer(BufferedOutputStream.java:65)
at java.io.BufferedOutputStream.flush(BufferedOutputStream.java:123)
at com.mysql.jdbc.MysqlIO.send(MysqlIO.java:3852)
... 81 more
cafe24는 크론탭을 지원해주지 않는다.
해당 방법이 많다고는 하지만 실질적으로 안된다.
log4j.rootLogger = DEBUG, stdout, rolling
log4j.appender.stdout = org.apache.log4j.ConsoleAppender
log4j.appender.console.Threshold =INFO
log4j.appender.stdout.layout = org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=[%5p] [%d][%t] %F.%M()-[%L Line] %m%n
log4j.appender.dailyfile.Threshold = DEBUG
log4j.appender.dailyfile = org.apache.log4j.DailyRollingFileAppender
# 로그가 쌓일 위치 적는다.
log4j.appender.rolling.File = /home/hosting_users/cafe24 계정/www/WEB-INF/logs/logfile.log
log4j.appender.dailyfile.layout = org.apache.log4j.PatternLayout
log4j.appender.dailyfile.layout.ConversionPattern=[%5p] [%d][%t] %F.%M()-[%L Line] %m%n
log4j.appender.rolling = org.apache.log4j.DailyRollingFileAppender
# 로그가 쌓일 위치 적는다.
log4j.appender.rolling.File = /home/hosting_users/cafe24 계정/www/WEB-INF/logs/logfile.log
log4j.appender.rolling.Append = true
log4j.appender.rolling.DatePattern = '.'yyyy-MM-dd
log4j.appender.rolling.layout = org.apache.log4j.PatternLayout
log4j.appender.rolling.layout.ConversionPattern=[%5p] [%d][%t] %F.%M()-[%L Line] %m%n
log4j.logger.org.mybatis=DEBUG
log4j.logger.org.apache=DEBUG
log4j.logger.java.sql=DEBUG
log4j.logger.scan하는패키지=DEBUG
CellReference.convertNumToColString 숫자를 엑셀의 숫자 Label 로 표기
CellReference.convertColStringToIndex 엑셀의 Cell Label 을 숫자로 표기. 일종의 Cell 번호.
https://poi.apache.org/apidocs/org/apache/poi/ss/util/CellReference.html
/**
* 엑셀에서 열 이름을 POI 에 맞도록 int 형으로 변환한다. ex) A -> 0, AA -> 26, AB -> 27
* @param cellNo
* @return
*/
public static int convertCellNameToInt(String cellNo){
int rvalue = 0;
char[] c = cellNo.toCharArray();
if ( c.length > 1 ) {
rvalue += (int)c[0]+26-65;
rvalue += (int)c[1]-65;
return rvalue;
} else {
rvalue = ((int)c[0])-65;
}
return rvalue;
}
엑셀의 사용자 정의 서식이 들어가 있는 날짜일경우
POI 관련자료를 찾아봤다...............
if( HSSFDateUtil.isCellDateFormatted(cell)) 이걸 사용하라고 하는데 -_-;;
안되는건지 되는건지...
일부 서식에서는 안된다.
결국 찾음.................
case HSSFCell.CELL_TYPE_NUMERIC:
if( HSSFDateUtil.isCellDateFormatted(cell)){
SimpleDateFormat fommatter = new SimpleDateFormat("yyyyMMdd");
data = fommatter.format(cell.getDateCellValue());
} else {
double ddata = cell.getNumericCellValue();
if ( HSSFDateUtil.isValidExcelDate(ddata) ){
SimpleDateFormat fommatter = new SimpleDateFormat("yyyyMMdd");
data = fommatter.format(cell.getDateCellValue());
} else {
data = String.valueOf(ddata);
}
}
(아래 부분은 직접 타이핑 한것 입니다;)
프록시 : 자신이 클라이언트가 사용하려고 하는 실제 대상인 것처럼 위장해서 클라이언트의 요청을 받아준다.
대리자, 대리인과 같은 역할을 한다고 해서 프록시(proxy)라고 부른다.
프록시의 특징은 타깃과 같은 인터페이스를 구현햇다는 것과, 프록시가 타깃을 제어할 수 잇는 위치에 있다는 것이다.
프록시는 사용목적에 따라 두 가지로 구분할수 있다.
a)클라이언트가 타깃에 접근하는 방법을 제어하기 위함(--> 프록시 패턴)
b)타깃에 부가적인 기능을 부여해주기 위함.(--> 데코레이터 패턴)
데코레이터 패턴: 타깃에 부가적은 기능을 런타임시(Runtime)시 다이내믹하게 부여해주기 위해 프록시를 사용하는 패턴
프록시 패턴:
프록시 패턴의 프록시는 타깃의 기능을 확장하거나 추가히지 않는다.
대신 클라이언트가 타깃에 접근하는 방식을 변경해준다.
타깃의 기능 자체에는 관여하지 않으면서 접근하는 방법을 제어해주는 프록시를 이용하는 것이다.
(따라서 프록시 패턴의 프록시는 코드에서 자신이 만들거나 접근할 타깃의 구체적인 클래스 정보를 알고 있는 경우가 많다.)
요약:
프록시 : 클라이언트와 사용 대상 사이에 대리 역할을 맡은 오브젝트를 두는 방법을 총칭
데코레이터 패턴 : 타깃에 부가적인 기능을 부여해주기 위해 프록시를 사용하는 패턴.
프록시 패턴 : 타깃에 대한 접근 방법을 제어하려는 목적을 가지고 프록시를 사용하는 패턴.
서브 출처 : http://blog.naver.com/ypark197?Redirect=Log&logNo=90093937410
AOP(Aspect Oriented Programming) in Java - Part I
Enterprise Java 환경에서 최근 가장 주목받는 기술 중 하나는 AOP, 즉 Aspected Oriented Programming 이다. AOP가 주목받는 이유는 Enterprise Java 시스템이 맞닥뜨리고 있는 복잡성때문이다.
(AOP의 정의에 대해서는 Wikipedia를 참조하기 바란다)
AOP는 전통적인 객체 지향의 프로그래밍(Object Oriented Programming. 이하 OOP)이 실패한 곳에서 진가를 발휘한다. OOP 언어들은 견고한 철학과 다양하고 유연한 프로그래밍 기법, 다양한 패턴들의 체계화등을 제공한다. 프로그래머들은 이로 인해 절차식 프로그래밍 언어들을 사용할 때에 비해 훨씬 직관적이고 체계적이고 모듈화된 구현을 할 수 있게 되었다. 만세!!!
하지만,불행히도 OOP 언어가 해결하지 못하는 몇가지 중요한 문제점들이 대두되었다.
가장 간단하고 직관적인 예로 로깅(Logging) 기능을 생각해 보자. OOP 언어의 대표격인 Java에서의 전통적인 Logging 구현 방법은 다음과 같다.
public class OrderManager {
public void orderItem(OrderItem oi) {
Logger log = Logger.getLogger("order");
log.info("orderItem started...");
// do something
try {
doSomething(oi);
} catch(Exception ex) {
log.info("Error : " + ex.getMessage());
} finally {
log.info("orderItem finished...");
}
}
Logger라는 객체를 이용해서 로깅 기능을 잘 모듈화했지만, 실제 이 객체를 사용하다보면 몇가지 심각한 문제를 만나게 된다.
- 로직을 구현하는 핵심 소스에 많은 수의 로깅 관련 소스가 삽입됨으로써 소스의 가독성과 간결함을 심하게 훼손한다.
- 로깅을 남기는 방법이나 시점, 위치 등을 바꾸려면 거의 모든 소스파일을 변경해야 한다.
위와 같이 "어떻게 하면 로깅을 남기는 기능을 핵심 로직과 무관하게 구분해서 구현할 수 있을까"라는 간단하고 명확한 요구에 대해 OOP는 적절한 대답을 하지 못한다.여기가 AOP가 개입되는 부분이다.
AOP는 객체를 프로그래밍 단위로 하지 않고, 특정 사건의 발생 양상(또는 상황/국면)을 프로그래밍의 단위로 한다. 이런 의미에서 AOP를 한글로 번역하자면상황 지향 프로그래밍이 가장 적절한 것으로 생각된다.
위의 로깅 예제에서는 다음과 같은 상황들이 펼쳐진다.
- orderItem 메소드가 시작하는 상황에 로그를 기록하라.
- orderItem 메소드가 끝나는상황에 로그를 기록하라.
- orderItem 메소드내에서 Exception이 발생하는 상황에 로그를 기록하라.
AOP에서는 위의 상황들을 정의할 수 있고, 이런 상황이 발생했을 때 수행할 작업을 정의할 수 있다. 즉, 핵심 로직과 무관하게 핵심 로직을 수행하는 과정에서 발생하는 상황들을 적절하게 다룰 수 있다.
Java AOP의 대표적인 언어인 AspectJ를 이용하면위의 소스를 다음과 같이 변경할 수 있다.
// 놀랍게도 핵심 로직에서는 Logging 관련된 부분이 완전히 제거된다.
public class OrderManager {
public void orderItem(OrderItem oi) {
// do something
try {
doSomething(oi);
} catch(Exception ex) { }
}
// 핵심 로직에서 발생하는 상황에 대한 로직을 구현한다.
public aspect LoggingAspect {
// OrderManger내에서 발생하는 모든 Method Call에 대해
pointcut methodExec() :
call(* *.*(..)) &&within(OrderManager);
//Method Call 시작시수행할 일
before() : methodExec() {
Logger log = Logger.getLogger("order");
log.info(thisJoinPointStaticPart.getSignature() + " started...");
}
//Method Call 종료시 수행할 일
after() returning : methodExec() {
Logger log = Logger.getLogger("order");
log.info(thisJoinPointStaticPart.getSignature() + " finished...");
}
// Exception 발생시
after() throwing(RuntimeException ex) : methodExec() {
Logger log = Logger.getLogger("order");
log.info("Error : " + ex.getMessage());
}
}
위와 같이 AOP를 이용하면 로깅과 같은 Aspect라는 이름으로 비핵심 로직을 핵심 로직에서 완전히 분리할 수 있으며, 이후 로깅과 관련된 변경 사항은 핵심 로직을 담당하는 소스의 변경이 아닌 Aspect의 변경만으로 이루어진다.
지금까지 AOP의 등장 배경에 대해 간략하게 살펴보았다. 다음 글을 통해 AOP의 개념 및 AOP의 대표 구현체인 AspectJ에 대해 좀 더 상세하게 논의해보자.
AOP의 개념 - part2
AOP에서는 위와 같이 핵심 로직 구현에서 부가적으로 처리해야 하는 작업들을 Concern(걱정/관심)이라고 부른다. Concern의 특징은 핵심 로직을 구현하는 여러 객체들에 걸쳐져 있다는 것이다. 가령 로깅 Concern은 핵심 로직을 구현하는 모든 객체와 관련되어 있다. 이런 의미에서는 흔히 Cross-cutting Concern이라는 용어를 쓴다. 우리말로 번역하면 "횡단-절단 관심" 정도가 될 것이다.
그림으로 표현하면 아래와 같지 않을까...?
즉 AOP는 객체가 구현하는 핵심 로직에 존재하는 여러 가지 Concern(관심거리, 혹은 걱정거리)을 구현하기 위한 프로그래밍 방법론으로 이해할 수 있다. 핵심 로직을 수행하는 과정에서 발생하는 로그 기록이라는 걱정 거리, 데이터베이스와의 통신 과정에서 발생하는 트랜잭션 관리라는 걱정 거리, 핵심 로직을 수행하는 과정에서 발생하는 성능을 측정하고자 하는 걱정 거리 등... 여러 객체와 행위를 관통하는(Cross-cutting)하는 걱정거리를 보다 손쉽게 구현하고자 하는 것이 바로 AOP의 핵심인 것이다.
AOP의 용어
AOP에 대해 더 상세하게 논의하기 전에 AOP에서 사용하는 용어에 대해 간략하게 정리해보자. 용어를 정확하게 이해해야만 AOP의 기법을 이해할 수 있다.
(정확하게 말하면 아래 용어는 AOP의 용어가 아니라 AspectJ의 용어이다. 하지만 AspectJ가 AOP의 사실상의 표준이기 때문에 무방하다고 간주한다)
- joinpoint : 프로그램 수행 과정에서의 특정 지점. 생성자의 호출, 메소드의 호출, 오브젝트 필드에 대한 접근 등의 대표적인 joinpoint들이다.
- pointcut : joinpoint와매칭하고자 하는 조건과 그 상황에서의 값들
- advice : pointcut에 의해매칭된 joinpoint에서 실행할 작업
- aspect : pointcut과 advice의 집합체. 즉 특정 상황(pointcut)과 그 상황에서 수행할 작업(advice)의 집합
- weaving : aspect과 핵심 로직을 섞는(weave) 것을 의미
이를 그림으로 표현하면 다음과 같다.
- 컴파일시 Weaving : Java/AspectA 소스파일을 컴파일하는 시점에 Weaving을 수행
- 로딩타임시 Weaving: Java Class 파일을 ClassLoader에 의해 로딩하는 시점에 Weaving을 수행
다음으로 계속...
AspectJ - part3
AspectJ는 Java에서의 AOP 언어의 사실상의 표준이다. Spring AOP, JBoss AOP 등 새로운 AOP 컴포넌트들이 있지만, 모두 AspectJ의 서브셋으로 보아도 무방할 것이다.
AspectJ의 특징과 역사는 Wikipedia에 잘 기술되어 있다. (고맙게도 ...)
현재 AspectJ은 Eclipse Project의 서브 프로젝트로 관리되고 있으며, IBM 영국 연구소의 개발자들이 핵심 멤버로 활동하고 있다. 따라서 Eclipse가 계속 존재하는 한, AspectJ 또한 계속 유지보수가 이루어질 것을 기대할 수 있다. ^^
AspectJ는 1.5 이후에 큰 변화를 겪었는데, 바로 AspectWerkz라는 이름의 AOP 프로젝트를 흡수한 것이다. 이 흡수로 인해 AspectJ는 1) 로드타임시 Weaving(Load Time Weaving. LTW) 지원, 2) Java 5 스타일의 Annotation 지원이라는 새롭고 강력한 특징을 가지게 되었다.
이 중, 특히 LTW 기능에 주목할 필요가 있다. 이전 버전의 AspectJ에서는 반드시 AspectJ가 제공하는 컴파일러를 이용해 사용자가 작성한 Class 파일이나 Jar 파일에 대해 Weaving 작업을 수행해야 했다. 한 프로젝트에서 모든 소스를 다 스스로 작성하는 경우는 모르겠지만, 다른 써드 파티가 제공하는 라이브러리를 사용하는 경우에는 확실히 번거로운 점이 있다.
LTW 기능을 이용하면 사용자의 Class 파일을 로딩하는 시점에(즉 실행시) Aspect를 Class와Weaving할 수 있다. 따라서 별도의번거로운 컴파일 과정을 거치지 않아도된다.
하지만!!! 속도 면에서는 컴파일시Weaving이 더유리할 수 있다는 점만은 기억해두자.특히 프로그램 실행 초기에 클래스가 로딩되는 속도가 눈에띄게느려질 수 있다는 점은 기억해둘 필요가 있다.
AspectJ 다운받고 사용하기
AspectJ는 http://www.eclipse.org/aspectj/index.php에서 다운받을 수 있다. 다음 두가지를 다운받아야 한다.
- AspectJ 1.5.3 - AspectJ 메인 릴리즈
- AJDT - AspectJ Development Tools. Eclipse Project내에서 AspectJ를 사용할 수 있는 플러그인을 제공
AJDT를 사용하면 Eclipse의 풍부한 기능과 함께 비주얼하게 AspectJ를 사용할 수 있으므로 보다 손쉽게 AspectJ에 접근할 수 있다.
AJDT를 설치하고 나면, 아래 그림과 같이 AspectJ를 기본으로 하는 프로젝트와 Aspect를 손쉽게 생성할 수 있다.
<< AspectJ 프로젝트 생성>>
<< Aspect 생성>>
다음 파트에서 AJDT를 이용해 간단한 AOP 샘플을 구현할텐데, 다음과 같은 Concern을 해결하는 것을 목표로 한다.
" 현재 운영 중인 시스템에서 코드상의 오류로 인해 Exception이 계속해서 발생한다. Exception이 발생할 때마다 발생한 Exception을 파악하고, Exceptioon의 발생 시점, 발생 이유, 발생 시의 Stack Trace 등을 기록하고자 한다."
즉, Exception 처리라는 걱정 거리를 좀 더 효과적으로 처리할려고 한다. 언뜻 어려워 보이는 이 걱정거리가 AOP에서는 얼마나 쉽게 해결가능한지 살펴보게 될 것이다.
AspectJ에서 Concern 구현 하기 - part4
아래에 심플한(?) 비지니스 로직을 구현하는 객체가 있다. 우리의 걱정 거리는 비지니스 로직에서 Exception이 발생할 때마다 상세한 발생 상황을 기록하는 것이다.
우리의 비지니스 로직은 다음과 같다.
이 비지니스 로직에 대한 우리의 Concern을 처리해야 하는 상황은 다음과 같다.
- ExceptionGenerator의 로직을 수행하는 과정에서 Exception이 발생하면 이것을 캡쳐해서 기록하고 싶다.
- 이 때 어떤 메소드를 호출하다가 Exception이 발생했는지, Exception의 종류는 무엇인지 등의 정보가 종합적으로 기록하고 싶다.
이 상황을 AOP 없이 처리하려면 제 아무리 자바의 고수라고 하더라도 다음과 같은 방식으로 일일이 소스를 변경해야 한다.
try { doSomething1() } catch(Exception ex) {
logger.log("Error " + ex + " occurred when executing ExceptionGenerator.doSomething1()...");
}
비록 Java Logging API나 Log4j 같은 라이브러리들이 이러한 작업을 처리하는데 상당히 도움이 되지만, 핵심 로직안에 우리의 Concern을 처리하는 로직을 넣어야 한다는 기본적인 사실에는 전혀 변화가 없다.
하지만, AspectJ를 사용하면... ? 핵심 로직에는 Exception Handling에 관련된 소스를 전혀 추가할 필요없이 다음과 같은 형태의 Aspect만을 만들어주면 된다.
Exception Aspect Version 1
매우 심플한 Aspect지만 두 가지의 핵심적인 정보를 담고 있는 완전한 형태의 Aspect이다.
- pointcut : 모든객체의 메소드콜을 횡단으로 매치시키는 call (* *.*(..)) 이라는 pointcut이 callpoint라는 이름으로 정의되어 있다.
- after advice : callpoint pointcut에서 Exceptoin이 발생한 이후(after + throwing) 수행할 advice가 정의되어 있다.
AspectJ에서는 대부분의 Concern이 pointcut과 advice의 조합으로 이루어진다. 즉 어떤 지점(pointcut)에서 어떤 일(advice)를 수행할 지가 바로 AspectJ가 구현하는 Concern에 대한 해결책이 된다.
위의 ExceptionAspect와 ExceptionGenerator를 Weaving해서 수행하면 다음과 같은 결과가 나온다.
결과1
이제 ExceptionAspect를 좀 더 다듬어서 보다 완전한 형태의 정보를 얻을 수 있도록 해보자.
Exception Aspect Version 1
더욱 세련된 모양의 Aspect가 구현되었음을 확인할 수 있다. Version 1에 비해 다음과 같은 특징들이 추가되었다.
- After advice에서 Exception발생시 Exception 객체를 받는다. 이렇게 받은 객체를 이용해서 필요한 정보를 추출한다.
- thisJoinPointStaticPart (또는 thisJoinPoint.getStaticPart())를 이용해 어떤 지점에서 발생한 Exception인지를 알아낸다.
위의 Aspect를 보고 "아... 정말 내가 원하던 방법론이다"라고 감탄을 했다면 이미 일류 프로그래머이거나 일류 프로그래머가 될 잠재력을 가지고 있는 사람일 것이다.
AspectJ 혹은 AOP를 현재 프로젝트에 사용하고 싶은 욕구가 이는가...!!!
AspectJ의 Load Time Weaving - part5
ApsectJ 1.5는 AspectWerkz라는 신흥 AOP 컴포넌트를 흡수하면서 Load Time Weaving 기능을 크게 향상시켰다. Load Time Weaving이란 말 그대로 클래스가 로드되는 시점에 Weaving 작업을 수행하는 것을 의미한다.
전통적으로 AspectJ에서는 ajc(AspectJ Compiler)라는 컴파일러를 사용해서 사용자가 작성한 Class 파일이나 Jar 파일을 컴파일 시간에 Weaving하는 방식을 지원했다. 비록 이 방법이 아직까지도 가장 보편적이고 또 편리한 방법이긴 하지만, 컴파일시 Weaving은 역시 불편한 방법이다. 하지만!!! 성능 면에서는 가장 유리한 방법이라는 것은 다시 한번 염두에 두자
AspectJ에서 Load Time Weaving이 적용되는 방식은 아래 그림과 같다.
aop.xml
aop.xml 파일은 LTW의 룰을 지정하는 역할을 한다. 즉 어떤 Aspect를 어떻게 Weaving 할 것인지 지정할 수 있다.
아래에 aop.xml의 간단한 예제가 있다.
위의 aop.xml 파일은[aop.ltw.SimpleLTWAspect]라는 이름의 Aspect를 사용하며, 이 Aspect를 이용해서 Weaving을 수행할 것을 지정한다.
AspectJ의 LTW 모듈은 [클래스패스(Classpath)/META-INF]에 있는 모든 aop.xml 파일을 불러와서 Weaving 작업을 수행한다.
aop.xml이 제공하는 문법은 매우 다양하고 강력하다. 어떤 Aspect를 어떤 타겟(비지니스 로직)에 대해 어떤 조건(pointcut)으로 사용할지를 자유롭게 지정할 수 있다. 예를 들어 Abstract Aspect를 만든 후 aop.xml에서pointcut을 정의할 수도 있다. aop.xml을 사용하는 상세한 방법은 AspectJ Manual을 참조한다.
LTW의 간단한 예제
아래에 간단한 Aspect가 있다.
이 Aspect의 역할은 Method의 시작과 끝을 잡아서 수행 시간을 측정하는 것이다. 어플리케이션 성능 측정을 위한 가장 기본적인 기능을 구현할 것이라고 볼 수 있다.
이 Aspect를 다음과 같이 ajc를 이용해서 컴파일한다.
c:aspectj1.5binajc -1.5 -cp ../..;c:aspectj1.5libaspectjrt.jar SimpleLTWAspect.aj
컴파일에 성공하면 SimpleLTWApsect.class 파일이 생긴다. 이 Aspect 파일과 위에서 샘플로 사용한 aop.xml 파일을 이용해서 LTW을 수행하는 명령어는 다음과 같다.
(SimpleLTW 객체는 몇 개의 메소드를 반복적으로 호출하는 단순한 객체이다)
java -javaagent:aspectjweaver.jar -cp ../.. aop.ltw.SimpleLTW
아래 결과를 보면 우리가 원하는 대로 각 메소드를 실행하는데 걸린 시간이 계산되어 나오는 것을 알 수 있다.
위에서 본 간단한예제만으로도 AspectJ에서 제공하는 LTW의 유연함과 강력함을 느낄 수 있으리라 믿는다.
PS)
Java 5 (JDK 1.5)부터는 java.lang.instrument 패키지를 이용해서 Byte Code Instrumentation(BCI)을 직접적으로 지원한다. 더 이상 BCI가 어둠의 자식이 아니라는 것을 의미한다.오히려 BCI가 Sun에서도 인정하는 보편적인 방법론임을 의미한다.
자연스럽게, AspectJ 1.5의 LTW도 이 기능을 이용한다. 위의 예에서 "-javaagent:aspectjweaver.jar" JVM 옵션이 java.lang.instrument 패키지를 이용한다는 것을 의미한다. 즉, aspectjweaver.jar 내에 클래스 로드 타임시 실시간으로 클래스를 Weaving하는 기능을 제공하는 Class Transformer가 존재한다.
JDK 1.4에서는 VM 레벨에서 BCI가 지원되지 않는다.JDK 1.4라면 아래와 같은 형식으로 사용가능하다.
java -classpath[aspectjweaver.jar]
-Djava.system.class.loader=org.aspectj.weaver.loadtime.WeavingURLClassLoader
-Daj.class.path=. -Daj.aspect.path=.[YourClass]
또는 JRockit에서는 다음과 같은 JVM옵션을 사용할 수 있다.
-Xmanagement:class=org.aspectj.weaver.loadtime.JRockitAgent
AOP(Aspect Oriented Programming) in Java - Part 6
AOP와 AspectJ를 이용해서 할 수 있는 일은 실로 무궁구진하다.
비지니스 로직을 처리하는 과정에서 발생하는 관심거리나 걱정거리를 좀 더 효율적이고 체계적으로 처리하고자 한다면 AOP가 바로 대답이다.
AOP를 이용해서 구현 가능한 몇 가지 사례를 끝으로 AOP에 관한 블로깅을 끝맺기로 한다.
- 로깅 : 각 조인포인트마다 적절한 로그를 남길 수 있다.
- 프로파일링 : 각 조인포인트마다 수행되는 메소드 정보를 기록해서 프로파일링을 구현할 수 있다. 가령 자바 어플리케이션에서 수행되는 모든 메소드에 대해 수행 시간을 측정하고자 한다면 AOP의 before/after/around advice를 이용해서 손쉽게 구현할 수 있다.
- 트랜잭션 관리 : 트랜잭션 관리에 관련된 모는 기능을 비지니스 로직에서 제거하고 AOP로 구현할 수 있다. EJB는 AOP의 개념을 사용하지 않고 EJB 컨테이너를 이용해서 트랜잭션을 관리하는 기능을 제공하는 것으로 이해할 수 있다. 반면 Spring과 같은 경량 프레임웍들을 EJB 컨테이너와 같은 무거운 방법대신 AOP를 사용해서 사용자가 작성한 비지니스 로직에 트랜잭션 관리에 필요한 Bytecode를 직접 삽입하는 방식을 사용한다.
- 코딩컨벤션 관리:클 래스의 필드명을 항상 m_ 로 시작하게끔 규칙을 부여한다고 하자. 여러 명의 개발자들이 개발을 진행할 경우 이 규칙이 지켜지기는 거의 불가능하다. 하지만 AOP의 필드 조인트포인트를 이용하면 컴파일 시점에 이러한 오류들을 모두 걸러낼 수 있다. AOP를 이용하면 매우 복잡한 코딩 컨벤션 관리를 거의 자동화 할 수 있다.
- 기타 등등... 오직 우리의 상상력에 의해서만 제약을 받는다!!!
국내 개발 프로젝트에서 AOP를 많이 사용하지 않는다고 해서 AOP가 무용하거나 현실과 동떨어진 것이라고 생각한다면 큰 오산이라는 것을 다시 한번 명심하자.
Spring 프레임웍이 내부적으로 트랜잭션 관리를 위해 AOP를 사용하고 있다. 따라서 여러분이 만일 Spring을 사용하고 있다면 이미 AOP를 사용하고 있는 것이다. 그 외에도 대부분의 Application Server가 AOP를 이용해 사용자가 작성한 클래스 파일에 특정 기능을 부여하는 기능을 곳곳에서 사용하고 있다.
앞서 다섯 편의 글을 통해 AOP의 강력함과 편리함을 조금이라도 느낄 수 있었기를 바라며, 국내의 개발 프로젝트에서 AOP를 사용하는 실제적인 사례를 목격하게 되기를 바래 본다.
import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
public class Test1 {
public static void main(String args[]) {
Test1.getDiskSpace();
}
//서버의 쉘(UNIX) 또는 배치파일(WINDOW) 실행결과 가져오기
public static void getDiskSpace() {
try {
Process ps = Runtime.getRuntime().exec("d:/temp/view_direcotory.cmd");
InputStream is = ps.getInputStream();
InputStreamReader reader = new InputStreamReader(is);
BufferedReader in = new BufferedReader(reader);
StringBuffer sBuffer = new StringBuffer();
char[] buf = new char[1024];
int readcnt;
while((readcnt = in.read(buf,0,1024)) !=-1)
{
sBuffer.append(buf, 0, readcnt);
}
in.close();
reader.close();
System.out.println(sBuffer.toString());
ps.destroy();
}
catch(Exception e) {
e.printStackTrace();
}
}
}
웹로직에서는 자체 Log4J 가 있기 때문에 별도 설정이 필요하다
override 하면 될것을...-_-;;
org.apache.log4j.PropertyConfigurator.configure(logPath); 이것으로 하단에 참조...
http://payoff.tistory.com/27
http://blog.naver.com/dalbong97/130021029647
1. log4j란?
log4j는 프로그래머가 로그문의 출력을 다양한 대상으로 할 수 있도록 도와주는 도구이다.
애플리케이션에 문제가 생겼을 때, 로깅을 활성화하면 문제의 위치를 찾을 수 있으므로 도움이 된다.
log4j를 사용하면 애플리케이션의 실행코드를 수정하지 않고 런타임에 로깅의 활성화를 할 수 있다.
log4j 패키지는 성능상의 높은 비용을 들이지 않으면서도 배포코드에 로그문장을 남겨놓을 수 있도록 디자인되었다.
그러므로 로깅의 속도(정확히 말하자면 로깅을 하지 않을 때의 속도)가 중요하게 다루어졌다.
동시에 로그 결과는 짧은 시간에 감당하기 어렵게 될만큼 커질 수 있다.
log4j의 독특한 특징 중 하나는 계층적 로거라는 개념이다.
로거를 사용하면, 어떤 로그문이 출력되게 할 지 임의의 단위로 조정할 수 있다.
2. log4j 구성
- Logger(Category)
로깅 메세지를 Appender에 전달합니다.
- Appender
전달된 로깅 메세지를 파일에다 기록할 것인지, 콘솔에 출력할 것인지 아니면 DB에 저장할 것인지 매개체 역활을 합니다.
- Layout
Appender가 어디에 출력할 것인지 결정했다면 어떤 형식으로 출력할 것이지 출력 layout을 결졍합니다.
3. log4j 로깅 레벨
로깅의 우선 순위에 따른 로깅 레벨이다.
FATAL > ERROR > WRN > INFO > DEBUG
① FATAL : 가장 크리티컬한 에러가 일어 났을 때 사용합니다.
② ERROR : 일반 에러가 일어 났을 때 사용합니다.
③ WARN : 에러는 아니지만 주의할 필요가 있을 때 사용합니다.
④ INFO : 일반 정보를 나타낼 때 사용합니다.
⑤ DEBUG : 일반 정보를 상세히 나타낼 때 사용합니다.
만약 현재 요청한 로깅의 우선순위가 카테고리의 우선순위와 같거나 높으면
그 로깅 요청이 가능하다고 말한다.
그렇지 않을 경우 그 요청은 불가능하다고 한다.
다음은 로깅 요청의 가능/불가능 여부가 어떻게 처리되는 지를 보여주는 예이다.
// 이름이 "com.foo"인 카테고리 인스턴스를 구한다.
Category cat = Category.getInstance("com.foo");
// 카테고리의 우선순위를 설정한다.
cat.setPriority(Priority.INFO);
// WARN >= INFO 이기 때문에, 이 요청은 가능하다.
cat.warn("Low fuel level.");
// DEBUG < INFO 이기 때문에, 이 요청은 불가능하다.
cat.debug("Starting search for nearest gas station.");
// 이름이 "com.foo.Bar"인 카테고리의 인스턴스를 생성한다.
// 이 카테고리는 이름이 "com.foo"인 카테고리를 상속 받는다.
// 따라서 이 카테고리 인스턴스는 INFO 우선순위를 갖는다.
Category barcat = Category.getInstance("com.foo.Bar");
// INFO >= INFO 이므로, 이 요청은 가능하다.
barcat.info("Located nearest gas station.");
// DEBUG < INFO 이므로, 이 요청은 불가능하다.
barcat.debug("Exiting gas station search");
4. log4j 설정
log4j 설정은 프로그램 내에서 할 수 있지만 설정파일을 사용함으로서 좀더 유연하게 log4j환경을 만들 수 있습니다.
① 프로그램에서 설정
<%@ page contentType="text/html;charset=MS949" import="org.apache.log4j.*,java.io.* " %> <%! <% PatternLayout patternlayout = new PatternLayout(layout); |
② property 파일에 설정
설정 파일의 위치를 별도로 지정하지 않으려면, log4j.properties를 만들어 /WEB-INF/classes 밑에 놓으세요.
* log4j.properties 파일은 /WEB-INF/classes/ 하위에 있어야 자동으로 load 됨.
log4j.rootLogger=INFO, stdout, rolling log4j.appender.stdout=org.apache.log4j.ConsoleAppender log4j.appender.stdout.layout=org.apache.log4j.PatternLayout log4j.appender.stdout.layout.ConversionPattern=%d %-5p [%t] %-17c{2} (%13F:%L) %3x - %m%n log4j.appender.rolling=org.apache.log4j.DailyRollingFileAppender log4j.appender.rolling.File=output.log log4j.appender.rolling.Append=true log4j.appender.rolling.MaxFileSize=500KB log4j.appender.rolling.DatePattern='.'yyyy-MM-dd log4j.appender.rolling.layout=org.apache.log4j.PatternLayout log4j.appender.rolling.layout.ConversionPattern=%d %-5p [%t] %-17c{2} (%13F:%L) %3x - %m%n
|
#최상위 카테고리에 INFO로 레벨 설정 및 appender로 stdout, rolling을 정의
log4j.rootLogger=INFO, stdout, rolling
#stdout 어펜더는 콘솔에 뿌리겠다는 정의
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
#stdout 어펜더는 patternlayout을 사용하겠다는 정의
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
#페턴은 다음과 같이 포맷팅 하겠다는 것을 정의
log4j.appender.stdout.layout.ConversionPattern=%d %-5p [%t] %-17c{2} (%13F:%L) %3x - %m%n
#역시나 rolling 어펜더는 파일로 처리한다라고 정의
log4j.appender.rolling=org.apache.log4j.DailyRollingFileAppender
#로그 파일 이름은 output.log
log4j.appender.rolling.File=output.log
#true면 톰캣을 내렸다 올려도 파일이 리셋되지 않습니다.
log4j.appender.rolling.Append=true
#파일 최대 사이즈는 500KB로 설정
log4j.appender.rolling.MaxFileSize=500KB
#파일 포맷은 output.log.2005-03-10 으로 관리하겠다고 정의
log4j.appender.rolling.DatePattern='.'yyyy-MM-dd
#역시나 rolling 어펜더는 패턴 레이아웃을 사용하겠다고 정의
log4j.appender.rolling.layout=org.apache.log4j.PatternLayout
#rolling 어펜더는 패턴 레이아웃 포맷
log4j.appender.rolling.layout.ConversionPattern=%d %-5p [%t] %-17c{2} (%13F:%L) %3x - %m%n
특정 위치의 설정파일을 지정하는 방법
1. WAS 기동 옵션으로
-Dlog4j.configuration=file:D:\StandardPrj\StandardProject\WebContents\WEB-INF\classes\log4j.properties
2. web.xml 파일에 설정
<context-param>
<param-name>log4jConfigLocation</param-name>
<param-value>/WEB-INF/log4j.properties</param-value>
</context-param>
3. 클래스에서 직접 로드
org.apache.log4j.PropertyConfigurator.configure(logPath);
VIII. 설정 포맷
① 로그파일명 포맷 (DatePattern)
로그파일명 포맷입니다. 날짜, 시간 및 분단위로까지 로그 파일을 분리할 수 있습니다.
형식 | 설명 |
'.'yyyy-MM | 매달 첫번째날에 로그파일을 변경합니다 |
'.'yyyy-ww | 매주의 시작시 로그파일을 변경합니다. |
'.'yyyy-MM-dd | 매일 자정에 로그파일을 변경합니다. |
'.'yyyy-MM-dd-a | 자정과 정오에 로그파일을 변경합니다. |
'.'yyyy-MM-dd-HH | 매 시간의 시작마다 로그파일을 변경합니다. |
'.'yyyy-MM-dd-HH-mm | 매분마다 로그파일을 변경합니다. |
② PatternLayout 포맷
로그자체를 어떤 포맷으로 남길지 결정합니다.
layout에는 HTMLLayout, PatternLayout, SimpleLayout, XMLLayout등이 있으며 PatternLayout이 일반적으로 가장 많이 쓰입니다.
형식 | 설명 |
%p | debug, info, warn, error, fatal 등의 priority 가 출력된다. |
%m | 로그내용이 출력됩니다 |
%d | 로깅 이벤트가 발생한 시간을 기록합니다. 포맷은 %d{HH:mm:ss, SSS}, %d{yyyy MMM dd HH:mm:ss, SSS}같은 형태로 사용하며 SimpleDateFormat에 따른 포맷팅을 하면 된다 |
%t | 로그이벤트가 발생된 쓰레드의 이름을 출력합니다. |
%% | % 표시를 출력하기 위해 사용한다. |
%n | 플랫폼 종속적인 개행문자가 출력된다. \r\n 또는 \n 일것이다. |
%c | 카테고리를 표시합니다 예) 카테고리가 a.b.c 처럼 되어있다면 %c{2}는 b.c가 출력됩니다. |
%C | 클래스명을 포시합니다. 예) 클래스구조가 org.apache.xyz.SomeClass 처럼 되어있다면 %C{2}는 xyz.SomeClass 가 출력됩니다 |
%F | 로깅이 발생한 프로그램 파일명을 나타냅니다. |
%l | 로깅이 발생한 caller의 정보를 나타냅니다 |
%L | 로깅이 발생한 caller의 라인수를 나타냅니다 |
%M | 로깅이 발생한 method 이름을 나타냅니다. |
%r | 어플리케이션 시작 이후 부터 로깅이 발생한 시점의 시간(milliseconds) |
%x | 로깅이 발생한 thread와 관련된 NDC(nested diagnostic context)를 출력합니다. |
%X | 로깅이 발생한 thread와 관련된 MDC(mapped diagnostic context)를 출력합니다. |
예시) (같은 색끼리 보시면 됩니다)
위의 test.jsp를 다음 포맷으로 출력해본다면
[%c] [%C] [%d] [%F] [%l] [%L] [%m] [%M] [%n] [%p] [%r] [%t] [%x] [%X]는 다음과 같다
[test.jsp] [org.apache.jsp.test_jsp] [2005-03-10 12:37:23,561] [test_jsp.java] [org.apache.jsp.test_jsp._jspService(test_jsp.java:64)] [64] [fatal!!] [_jspService] [개행] [FATAL] [765567] [http-8080-Processor25] [] []
=============================================
본문서는 자유롭게 배포/복사 할수 있지만
이문서의 저자에 대한 언급을 삭제하시면 안됩니다
저자 : GoodBug (unicorn@jakartaproject.com)
최초 : http://www.jakartaproject.com
=============================================
log4j를 적용한 로깅 예제
// 로거 임포트하기
import org.apache.log4j.Logger;
public class ClassName {
/** Log4J 로거 얻기 */
private Logger log = Logger.getLogger(ClassName.class);
public void method() {
if (log.isDebugEnabled()) {
log.debug("디버깅용 메시지");
}
log.info("정보를 남기기위한 메시지");
try {
// 어쩌구 저쩌구 실행...
} catch (Exception ex) {
// 로그에 예외 메시지도 함께 남기기
log.error("예외가 발생했어요~~", ex);
}
}
}
--------------------------------------------------------------------------------
프로바이더 mailto: madvirus@madvirus.net:
현재 티페이지 글로벌(주)의 기술연구소에 있으며,
'JSP 웹 어플리케이션 개발(가메출판사)'을 이동훈씨와 함께 저술한 바 있다.
--------------------------------------------------------------------------------
관련자료
예제 소스 코드
관련링크
Log4j 프로젝트 홈페이지
http://jakarta.apache.org/log4j/index.html
Sun의 로깅 API 규약
http://java.sun.com/aboutJava/communityprocess/jsr/jsr_047_log.html
실질적으로 테스트 해보지는 않았습니다.
훗날 사용할거 같아서...
-----------------------------------------------------------------------------------------
Google Blogger API는 ATOM 이라고 하는 표준(아직도 진행중) 을 이용하여 인증, 조회, 수정, 생성을 할 수 있도록 방법을 제공한다. 하지만 처음 API를 사용하여 Google Blogger를 제어하는 사용자에게는 예제의 부적절함 혹은 부족으로 상당한 시행착오를 겪는다 물론 그렇지 않은 사람들도 있다.
혹시 다른 분들도 시행착오를 겪을 수 있으므로 조금이나 도움이 되었으면 좋겠다.
Google Blogger API : http://code.blogger.com/archives/atom-docs.html
개발환경 : Jdk1.5와 HttpClient3.0
기본적으로 읽기/쓰기 등을 하기 위해서는 먼저 인증절차를 거쳐야 한다.
이 인증은 모든 atom request 에 적용되어야 한다.
Google Blogger는 모든 ATOM reqeust 를 HTTP Basic Authentication을 SSL 방식으로 인증한다.
Google Blogger API 의 자료를 보면 아래와 같이 사용자의 블로그 리스트를 얻어 오는데
Get a List of a User's Blogs:
GET /atom HTTP/1.1
Host: www.blogger.com
Authorization : BASIC c3RldmVqOm5vdGFyZWFscGFzc3dvcmQ=
이 때 Authorization: BASIC c3RldmVqOm5vdGFyZWFscGFzc3dvcmQ= 이 부분이 사용자 아이디와 패스워드로 생성된 credential이다. 이 Credential을 이용하여 google blogger server와의 인증을 하게 된다.
Http Request를 위와 같은 형태로 생성하여 보내줄 수만 있으면 되는 것이다.
그럼 위의 내용을 HttpClient를 이용하여 구현하면 아래와 같이 mehtod를 구현할 수 있다.
public String getAuthByAtom() {
HttpClient client = new HttpClient();
// userid, passwd는 실제로 존재하는 것을 사용한다.
// 빨간글자의 코드가 시행착오를 겪었던 부분임.
client.getState().setCredentials(
new AuthScope("www.blogger.com", 443, "Blogger"),
new UsernamePasswordCredentials("userid", "passwd")
);
// user의 블로그 리스트를 조회하는 url 임
GetMethod get = new GetMethod("https://www.blogger.com/atom");
get.setDoAuthentication( true );
try {
int status = client.executeMethod(get);
System.out.println(status + "\n" + get.getResponseBodyAsString());
} catch (Exception e) {
System.out.println(e.toString());
} finally {
get.releaseConnection();
}
return get.getResponseBodyAsString()
}
위의 코드는 결과로 아래와 같은 XML을 return 한다.
특정 user가 가지고 있는 블로그의 리스트이다.
<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<feed xmlns="http://purl.org/atom/ns#">
<userid xmlns="http://www.blogger.com/atom/ns#">27275760</userid>
<link href="https://www.blogger.com/atom/30952142" rel="service.post" title="Open everything" type="application/atom+xml"/>
<link href="https://www.blogger.com/atom/30952142" rel="service.feed" title="Open everything" type="application/atom+xml"/>
<link href="http://kimkun.blogspot.com" rel="alternate" title="Open everything" type="text/html"/>
</feed>
이 api를 사용하는 개발자들은 return 되어 오는 xml 데이타를 적절히 파싱하여 사용하면 될 것이다.
다른 Google Blogger API도 위와 같은 원리로 http basic authentication을 이용하여 인증절차를 거치고특정 url을 호출하여 원하는 기능을 수행하도록 한다.
인증은 위의 코드로 수행을 하고 ROME(http://wiki.java.net/bin/view/Javawsxml/Rome) 을 이용하여 xml format으로 생성하거나 parsing을 하면 xml 관련된 작업을 수월하게 할 수 있을 것이다.
[출처] Google Blogger API 사용|작성자 김군
public static void arraycopy(Object sarr, /*원본배열.*/
int st_position, /*원본배열의 시작위치*/
Object earr, /*복사할 배열*/
int ed_position, /* 복사할 배열의 시작위치 */
int length) /*복사할 개수.*/
System.arraycopy(a, 2, b, 3, 4);
위와 같이 arraycopy를 이용하는 방법과 다음의 clone을 이용하는 방법이 있습니다.
int[] a = (int[])b.clone();
출처 : http://yayongi.tistory.com/category/Programming/JAVA
metaweblog api 사용하여 티스토리에 게시물 작성하기
xml-rpc 3.1 사용
*/
import java.util.*;
import org.apache.xmlrpc.client.XmlRpcClient;
import org.apache.xmlrpc.client.XmlRpcClientConfigImpl;
// The location of our server.
private final static String SERVER_URL ="http://name.tistory.com/api";
//ex) 티스토리 환경설정 > 기타설정 하단에 있어요.
try {
XmlRpcClientConfigImpl config = new XmlRpcClientConfigImpl();
//구버젼에선 XmlRpcClient 객체를 생성할때 주소를 지정했지만 xmlrpc 3.x대에선
//XmlRpcClientConfigImpl 객체를 통해서 지정하는거 같습니다.
config.setServerURL(new java.net.URL(SERVER_URL));
Vector params = new Vector();
//ex) 143222 티스토리 환경설정 > 기타설정 하단에 있어요.
params.addElement(new String("Tistory 로그인 아이디"));
//ex) 티스토리는 이메일주소를 쓰죠?
params.addElement(new String("Tistory 비밀번호"));
//비밀번호 다들 아시죠?
Hashtable hashtable = new Hashtable();
hashtable.put( "title", "제목을 입력하세요" );
hashtable.put( "description", "내용을 입력하세요." );
hashtable.put( "mt_keywords", "태그를 입력하세요.");
String category[] = new String[1];
category[0] = "분류이름을 입력하세요";
hashtable.put( "categories", category);
params.addElement(hashtable );
params.add( new Boolean( true ));
//true몇 공개 false면 비공개
//포스팅된 번호가 나옵니다.
} catch (XmlRpcException exception) {
System.err.println("JavaClient: XML-RPC Fault #" + Integer.toString(exception.code) + ": " + exception.toString());
} catch (Exception exception) {
System.err.println("JavaClient: " + exception.toString());
}
}
}
public static boolean isNumber(String number){
boolean flag = true;
if ( number == null || "".equals( number ) )
return false;
int size = number.length();
int st_no= 0;
if ( number.charAt(0) == 45 )//음수인지 아닌지 판별 . 음수면 시작위치를 1부터
st_no = 1;
for ( int i = st_no ; i < size ; ++i ){
if ( !( 48 <= ((int)number.charAt(i)) && 57>= ( (int)number.charAt(i) ) ) ){
flag = false;
break;
}
}
return flag;
}
출처 : http://www.okjsp.pe.kr
import java.util.StringTokenizer;
public class CommString {
/*
* 문자열을 16진수 유니코드로 변경
*/
public static String StrtoUni(String str) {
String uni = "";
for (int i = 0; i < str.length(); i++) {
char chr = str.charAt(i);
String hex = Integer.toHexString(chr);
uni += "\\u" + hex;
}
return uni;
}
/*
* 16진수 유니코드를 문자열로 변경
*/
public static String UnitoStr(String uni) {
String str = "";
StringTokenizer str1 = new StringTokenizer(uni, "\\u");
while (str1.hasMoreTokens()) {
String str2 = str1.nextToken();
int i = Integer.parseInt(str2, 16);
str += (char) i;
}
return str;
}
}
[출처] encodeURIComponent 로 인코딩 한 후 서버로 전송했을 때 한글이 깨지는 문제.|작성자 희주
encodeURIComponent는 javascript에서 string을 UTF-8로 인코딩해주는 함수이다.
보통 http 로 주소를 전송할 때, 넘겨야 하는 변수 값 중간에 &나 #이 있으면 변수값이 제대로 전달되지 않기 때문에
인코딩을 해줘야 할 필요성이 생기는데, javascript에서 인코딩 하는 방식은 몇가지가 더 있지만,
서버에서 해석이 가능한 인코딩은 encodeURIComponent 함수를 이용하는 방법 뿐이다.
그러나 서버의 기본 문자 인코딩 타입이 UTF-8이라면 아무런 문제 없이 한글이 깨지지 않지만,
그 외의 인코딩 타입이 기본 문자셋이라면 한글이 깨지게 된다. (특히 서버가 MS949로 되어있는 경우-)
왜 깨지는 것일까-? 복원할 방법은 없는 것일까.. 를 고민했다.
문자가 깨지는 상황을 재현하기 위해 테스트 파일을 만들어봤다.
public void test() throws UnsupportedEncodingException {
String a = "건강·의학";
a = URLEncoder.encode(a, "UTF-8");
log.info("encode : " + a); //%EA%B1%B4%EA%B0%95%C2%B7%EC%9D%98%ED%95%99
a = URLDecoder.decode(a, "MS949");
log.info("decode : " + a); //嫄닿컯쨌?쓽?븰
a = URLEncoder.encode(a, "MS949");
log.info("encode : " + a); //%EA%B1%B4%EA%B0%95%C2%B7%3F%9D%98%3F%95%99
a = URLDecoder.decode(a, "UTF-8");
log.info("decode : " + a); //건강·??????
}
처음에 UTF-8로 인코딩 한 상태가 javascript에서 encodeURIComponent함수를 이용하여 인코딩한 결과와 같다.
그리고 서버에서는 MS949로 디코딩을 자동으로 수행하게 된다. (서버의 기본 캐릭터 셋이 MS949일 때)
그래서 결국 "건강·의학"이라는 글자는 "嫄닿컯쨌?쓽?븰" 이라는 알 수 없는 알 수 없는 글자로 깨지게 되고,
이를 다시 복구하기 위해 MS949로 인코딩을 해보았지만,
처음 javascript에서 encodeURIComponent 함수를 이용하여 인코딩 한 결과와 정확히 일치하지 않는다.(위에 빨간글자)
UTF-8로 인코딩한 한글 문자열을 MS949로 디코딩할 때는 이미 캐릭터의 byte값이 알 수 없는 정보로 저장되며,
다시 인코딩을 한다고 해서 원복이 되지 않는 것 같다.
해결방법은 아래 3가지이다.
1. UTF-8로 인코딩 하지 않고 그냥 전송한다.
이 방법은 변수의 값을 온전하게 전송할 수 없기 때문에 문제가 된다.
2. MS949로 인코딩하여 전송한다.
불행히도 javascript에서는 MS949인코딩을 지원하지 않는다.
구글링을 해보면 개인적으로 javascript의 인코딩을 지원해주는 유틸들을 만들어서 배포한 걸 볼 수 있는데, 완벽하지 않다. 잘못 쓰면 낭패본다.;;
서버에서 MS949로 인코딩하여 내려보내주고, 그 데이터를 활용하면 되지만,
html에서 script로 동적으로 생성되는 문자열을 인코딩해야 한다면, ajax를 활용해서 서버로부터 MS949변환된 문자열을 가져와야 한다.
하지만 매번 ajax 호출이 힘든 환경도 있을 것이다..
3. encodeURIComponent 함수를 두번 사용하여 UTF-8로 두번 인코딩 한 후에 서버에서 UTF-8로 한번 더 인코딩을 한다.
아래 코드를 참고한다.
public void test() throws UnsupportedEncodingException {
String a = "건강·의학";
a = URLEncoder.encode(a, "UTF-8");
a = URLEncoder.encode(a, "UTF-8");
log.info("encode : " + a); //%25EA%25B1%25B4%25EA%25B0%2595%25C2%25B7%25EC%259D%2598%25ED%2595%2599
a = URLDecoder.decode(a, "MS949");
log.info("decode : " + a); //%EA%B1%B4%EA%B0%95%C2%B7%EC%9D%98%ED%95%99
a = URLDecoder.decode(a, "UTF-8");
log.info("decode : " + a); //건강·의학
}
두번 인코딩을 하기 때문에 변수 명도 길어지고, 받는 쪽에서도 디코딩을 한번은 해야 하기 때문에 그다지 나이스한 방법은 아니지만,
어쨌든 문제 해결은 되고, 코드 복잡도는 적은 편이며, 왜 이런 행위를 했는지만 잘 적어놓으면 된다. -_-;;
위와 같은 꽁수가 통하는 이유는, 영문과 숫자, %로만 이뤄진 문자열을 UTF-8로 인코딩한 문자열은
MS949로 디코딩하나, UTF-8로 디코딩하나 결과는 동일하기 때문이다.
인코딩의 세계는 쉽지 않다;;
출처 : http://blog.daum.net/7dbwnckd/7972610
1. OSGi 란 무엇인가?
- Open Services Gateway initiative
- Dynamic Module System for Java
요약하면, OSGi 는 한 개의 번들 또는 여러 개의 번들로 이루어진 애플리케이션 자체를 언제든지 동적으로 프레임워크상에 설치, 실행, 업데이트, 중단, 제거하는 것을 가능하게 하는 매우 유연한 라이프 사이클 모델을 지원하는 프레임워크이다.
2. OGSi 의 특징
바이트코드와 가상머신 기술을 이용하여 코드 호환성을 보장하는 자바 플랫폼 위에서, 각 애플리케이션들이 번들이라 불리는 작고 재사용 가능한 컴포넌드로부터 조립될 수 있도록 도와준다. 번들은 OSGi에서 얘기하는 각각의 컴포넌트 똔느 애플리케이션을 가리키는 단위를 의미한다. OSGi는 JVM 위에서 돌아가는 하나의 프레임워크이며, 사용자가 개발한 프로그램들은 번들 형태로 선언되어 OSGi 내부에서 실행된다.
3. OSGi 아키텍쳐
3-1. OSGi 프레임워크
* Secutiry Layer : 자바의 보안 구조에 기반하고 있으며, 패키지나 서비스에 대한 권한을 관리하거나, Digitally Signed JAR 파일에 대한 지우너을 해주는 레이어이다. 꼭 사용하지 않아도 되는 선택가능한 레이어이다.
* Service Layer : 서비스 레지스트리 를 통해 서비스를 등록하고 찾을 수 있도록 지원하는 레이어이다.
* Life Cycle Layer : 번들이 어떻게 동적으로 설치되고 관리될 수 있는지를 정의하는 레이어이다. 번들 내에서 어떻게 외부의 OSGi Context에 접근할 수 있는지를 정의함.
* Module Layer : OSGi의 근간이 되는 번들
* Execution Environment : 번들이 수행될 수 있는 환경(J2SE등)
필자의 경우 yessign에서 발급한 은행용 공인인증서를 가지고 있는데 그 경로는 C:\NPKI\yessign\USER\아래폴더... 에 위치해 있다.
그 디렉토리에 보면 CaPubs, signCert.der, signPri.key 세 파일이 존재한다.
CaPubs은 무슨 파일인지 잘 모르겠다. signCert.der는 공인 인증서 파일이고, signPri.key는 개인키 파일이다.
(der은 인증서 저장시 바이너르 형태로 저장하기 위한 포맷이고, pem은 문자열로 표현가능한 데이터로 인코딩(BASE64같은..)한 포맷이다.)
한국정보보호진흥원(http://www.rootca.or.kr/kcac.html)의 기술규격을 참조해보면, 현재 사용하는 공인인증서는 RFC3280을 준수하여, 전자서명인증체계에서 사용하는 정수2를 갖는 X.509 v3을 사용하고 있다고 한다.
1. 공개키 가져오기.
- 자바에서 X.590를 지원해주니 간단히 사용해보자.
package test.security; import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.security.cert.CertificateFactory; import java.security.cert.X509Certificate; public class CertificateTest1 { public static void main(String[] args) throws Exception { X509Certificate cert = null; FileInputStream fis = null; try { fis = new FileInputStream(new File("C:/signCert.der")); CertificateFactory certificateFactory = CertificateFactory.getInstance("X509"); cert = (X509Certificate) certificateFactory.generateCertificate(fis); } finally { if (fis != null) try {fis.close();} catch(IOException ie) {} } System.out.println(cert); System.out.println("-----------------"); System.out.println(cert.getPublicKey()); } }
실행해보면 아래처럼 인증서에 대한 정보를 볼 수 있을것이다.(보안 관계상 많은 부분을 생략하겠다.)
[
[
Version: V3
Subject: CN=누굴까(RangWoo)0000000000000000, OU=XXX, OU=personalXXX, O=yessign, C=kr
Signature Algorithm: SHA1withRSA, OID = 1.2.840.113549.1.1.5
Key: Sun RSA public key, 1024 bits
... 생략 ...
[7]: ObjectId: 1.3.6.1.5.5.7.1.1 Criticality=false
AuthorityInfoAccess [
[accessMethod: 1.3.6.1.5.5.7.48.1
accessLocation: URIName: http://ocsp.yessign.org:4612]
]
... 생략 ...
Sun RSA public key, 1024 bits
modulus: 00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
public exponent: 00000
당연히, V3 버젼을 사용하고 서명 알고리즘은 SHA1withRSA을 사용한다. SHA1withRSA 옆에 보면 OID란 놈이 있다.
OID란 Object IDentifier의 약어로서 객체식별체계정도로 이해하면 되겠다. 즉, OID의 값이 1.2.840.113549.1.1.5이면 SHA1withRSA란 의미이다.
http://www.oid-info.com/ 사이트에 가서 1.2.840.113549.1.1.5 값을 입력하면 아래와 같은 값을 얻을 수 있다.
그리고 중간쯤에 ocsp(Online Certificate Status Protocol)라고 실시간으로 인증서 유효성 검증을 할수 있는 정보도 나온다.
좀더 내려가보면 공개키부분이 나오는데, 이놈이 우리가 사용할 부분이다. cert.getPublicKey() 메소드를 이용하면 직접 공개키를 가져올 수 있다.
2. 개인키 가져오기
- 공개키는 거의 날로 먹었는데, 개인키란 놈은 만만하지가 않다.
- 기본적으로(?)는 PKCS#8를 이용해서 개인키를 저장하는데, 국내 공인인증서에 사용하는 개인키 파일는 암호화(?)해서 저장한다.
PKCS#5(Password-Based Cryptography Standard)의 PBKDF1(Password-Based Key Derivation Function), PBES1(Password-Based Encryption Scheme)를 이용한다는 것이다.
여기까지는 별 문제가 없는데, 데이터 암호화를 할때 국내에서만 사용하는 SEED란 블럭암호화 알고리즘를 사용한다는것이다.
즉, 기본적으로 제공이 안되므로 직접 구현을 해야한다.
뭔소리인지 이해가 안가면 한국정보보호진흥원(http://www.rootca.or.kr/)의 암호 알고리즘 규격(KCAC.TS.ENC)를 한번 읽어보자. (사실 읽어봐도 이해가 안가지만... ^^;)
간단히 설명을 하자면, PBES(Password-Based Encryption Scheme) 즉 패스워드 기반의 키 암호화 기법을 사용하겠다는 것이다. 암호화 할때 필요한게 비밀키이다. 이 키는 해당 알고리즘에 맞는 바이트 배열로 보통 사용을 하는데, 이것을 사람이 쉽게 인식할 수 있는 패스워드로 사용하겠다는것이다.
뭐 필자처럼 무식하게 "hello123".getBytes(); 를 사용해서 키로 사용할 수 있지만, 모양새가 안좋아보인다는것이다. 그래서 "hello123" 문자열을 가공해서 멋진(?) 키로 만들어 사용한다는 것이다.
이 가공하는 함수가 PBKDF(Password-Based Key Derivation Function)이다. 그리고 이 함수를 이용해서 비밀키를 생성해서 암호화/복화하는 하는 구조를 PBES라고 한다.
자바에서 기본적으로 "PBEWithMD5AndDES", "PBEWithSHA1AndDESede" 등의 알고리즘을 제공해준다.
Security.getProviders(); 메소드를 이용해서, Provider 정보를 출력해보면 지원하는 알고리즘을 알 수 있다.
package test.security; import java.security.Provider; import java.security.Security; public class ProviderInfo { public static void main(String[] args) { Provider[] providers = Security.getProviders(); for (int i = 0; i < providers.length; i++) { String name = providers[i].getName(); String info = providers[i].getInfo(); double version = providers[i].getVersion(); System.out.println("--------------------------------------------------"); System.out.println("name: " + name); System.out.println("info: " + info); System.out.println("version: " + version); for (Object key : providers[i].keySet()) { System.out.println(key + "\t"+ providers[i].getProperty((String)key)); } } } }
그런데 불행히도 "PBEWithSHA1AndSeed"같은 알고리즘은 없는거 같다. 어떻게 해야할까? 당연히 삽~을 들어야한다.(아~~ 또 무덤을 파는구나 ㅠㅠ)
일단 파일의 구조를 파악해서 필요한 정보를 읽어와야한다.(ASN. 1으로 인코딩되어있다.)
다행히도 PKCS#8로 정의하고 있는 구조를 읽을 수 있는 EncryptedPrivateKeyInfo 클래스가 존재해서 한결 쉽게 작업을 할 수 있다
EncryptedPrivateKeyInfo 클래스를 사용해서 정보를 읽어오자. 사용하는 알고리즘을 출력해 보자.
// 1. 개인키 파일 읽어오기 byte[] encodedKey = null; FileInputStream fis = null; ByteArrayOutputStream bos = null; try { fis = new FileInputStream(new File("C:/signPri.key")); bos = new ByteArrayOutputStream(); byte[] buffer = new byte[1024]; int read = -1; while ((read = fis.read(buffer)) != -1) { bos.write(buffer, 0, read); } encodedKey = bos.toByteArray(); } finally { if (bos != null) try {bos.close();} catch(IOException ie) {} if (fis != null) try {fis.close();} catch(IOException ie) {} } System.out.println("EncodedKey : " + ByteUtils.toHexString(encodedKey)); // 2. 개인카 파일 분석하기 EncryptedPrivateKeyInfo encryptedPrivateKeyInfo = new EncryptedPrivateKeyInfo(encodedKey); System.out.println(encryptedPrivateKeyInfo); System.out.println(encryptedPrivateKeyInfo.getAlgName());
필자의 경우 "1.2.410.200004.1.15"란 값을 얻을 수 있었다. 나머지 파라메터 정보는 불행히도 제공을 안해줘서 직접 처리해야한다.
"1.2.410.200004.1.15" 어디서 많이 본 형식이다. 그렇다. OID이다. 사이트(http://www.oid-info.com/)가서 조회를 해보자.
"Key Generation with SHA1 and Encryption with SEED CBC mode" 란다.
한국정보보호진흥원(http://www.rootca.or.kr/)의 암호 알고리즘 규격(KCAC.TS.ENC)에서도 해당 OID에 대한 정보를 알 수 있다.
즉, 두 번째 방법이라는 것인데, DK의 값을 이용해서 해쉬값을 만든다음 그 값을 IV(초기화 벡터)로 사용하라는 것이다.
여기서 DK란 PBKDF를 사용해서 만든 추출키를 의미한다. 그렇다면 먼저 추출키를 만들어보자.
위의 설명대로 해당 함수를 구현해보자.
salt와 iteration count가 필요하다.
salt는 공인인증서를 발급할때마다 랜덤하게 생성되는것으로, 블특정다수의 사전(Dictionary) 공격을 방지하는 역할을 한다.(21-28바이트 사이의 8바이트를 사용함)
iteration count는 비밀키 생성을 위해 해쉬함수를 몇번 반복할 것인가를 나타낸다. (31-32바이트 사이의 2바이트를 사용함)
byte[] salt = new byte[8]; System.arraycopy(encodedKey, 20, salt, 0, 8); System.out.println("salt : " + ByteUtils.toHexString(salt)); byte[] cBytes = new byte[4]; System.arraycopy(encodedKey, 30, cBytes, 2, 2); int iterationCount = ByteUtils.toInt(cBytes); System.out.println("iterationCount : " + ByteUtils.toHexString(cBytes)); System.out.println("iterationCount : " + iterationCount);
그럼 PBKDF1을 구현해보자. RFC2898(http://www.ietf.org/rfc/rfc2898.txt)을 보면 아래처럼 설명이 나와있다.
5.1 PBKDF1 PBKDF1 applies a hash function, which shall be MD2 [6], MD5 [19] or SHA-1 [18], to derive keys. The length of the derived key is bounded by the length of the hash function output, which is 16 octets for MD2 and MD5 and 20 octets for SHA-1. PBKDF1 is compatible with the key derivation process in PKCS #5 v1.5. PBKDF1 is recommended only for compatibility with existing applications since the keys it produces may not be large enough for some applications. PBKDF1 (P, S, c, dkLen) Options: Hash underlying hash function Input: P password, an octet string S salt, an eight-octet string c iteration count, a positive integer dkLen intended length in octets of derived key, a positive integer, at most 16 for MD2 or MD5 and 20 for SHA-1 Output: DK derived key, a dkLen-octet string Steps: 1. If dkLen > 16 for MD2 and MD5, or dkLen > 20 for SHA-1, output "derived key too long" and stop. 2. Apply the underlying hash function Hash for c iterations to the concatenation of the password P and the salt S, then extract the first dkLen octets to produce a derived key DK: T_1 = Hash (P || S) , T_2 = Hash (T_1) , ... T_c = Hash (T_{c-1}) , DK = Tc<0..dkLen-1> 3. Output the derived key DK.설명대로 구현해주자. 피곤한 관계상 SHA1을 사용해서 20바이트의 추출키만을 반환하도록 만들었다.
public static byte[] pbkdf1(String password, byte[] salt, int iterationCount) throws NoSuchAlgorithmException { byte[] dk = new byte[20]; MessageDigest md = MessageDigest.getInstance("SHA1"); md.update(password.getBytes()); md.update(salt); dk = md.digest(); for (int i = 1; i < iterationCount; i++) { dk = md.digest(dk); } return dk; } }
해당 함수를 사용해서 추출키(DK) 초기화 벡터(IV)를 만들어 보자.
String password = "password"; // 추출키(DK) 생성 byte[] dk = pbkdf1(password, salt, iterationCount); System.out.println("dk : " + ByteUtils.toHexString(dk)); // 생성된 추출키(DK)에서 처음 16바이트를 암호화 키(K)로 정의한다. byte[] keyData = new byte[16]; System.arraycopy(dk, 0, keyData, 0, 16); // 추출키(DK)에서 암호화 키(K)를 제외한 나머지 4바이트를 SHA-1 // 으로 해쉬하여 20바이트의 값(DIV)을 생성하고, 그 중 처음 16바이트를 초기 // 벡터(IV)로 정의한다. byte[] div = new byte[20]; byte[] tmp4Bytes = new byte[4]; System.arraycopy(dk, 16, tmp4Bytes, 0, 4); div = SHA1Utils.getHash(tmp4Bytes); System.out.println("div : " + ByteUtils.toHexString(div)); byte[] iv = new byte[16]; System.arraycopy(div, 0, iv, 0, 16); System.out.println("iv : " + ByteUtils.toHexString(iv));
당연히 password 변수에는 공인인증서 암호를 입력해야한다. 안그러면 에러가 난다.
이제 고지가 눈앞에 보인다. 남은것은 SEED를 이용해서 복화만 하면 되는것이다. SEED 구현 + CBC 운용모드 구현을 직접하려면 정신적인 데미지가 커질 수 있으므로, 만들어놓은것을 가져다 쓰겠다.
Bouncy Castle Crypto APIs(http://www.bouncycastle.org/)를 감사하는 마음으로 가져다 쓰자.
%JAVA_HOME%/jre/lib/ext에 해당 jar파일을 복사한 다음, %JAVA_HOME%/jre/lib/security/java.security 파일에
security.provider.7=org.bouncycastle.jce.provider.BouncyCastleProvider
을 추가해서 사용할 수 있지만, 귀찮은 관계로 그냥(?) 사용하겠다.
// 3. SEED로 복호화하기 BouncyCastleProvider provider = new BouncyCastleProvider(); Cipher cipher = Cipher.getInstance("SEED/CBC/PKCS5Padding", provider); Key key = new SecretKeySpec(keyData, "SEED"); cipher.init(Cipher.DECRYPT_MODE, key, new IvParameterSpec(iv)); byte[] output = cipher.doFinal(encryptedPrivateKeyInfo.getEncryptedData());
이젠 해당 데이터로 개인키를 생성만 해주면 된다.
PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(output); KeyFactory keyFactory = KeyFactory.getInstance("RSA"); RSAPrivateCrtKey privateKey = (RSAPrivateCrtKey)keyFactory.generatePrivate(keySpec); System.out.println(privateKey);패스워드를 일치여부는 PBES에서 정의한 패딩이 존재하는지 여부로 판단한다. 만약 잘못된 패스워드라면
Exception in thread "main" javax.crypto.BadPaddingException: pad block corrupted
같은 에러가 발생할것이다.
그럼 마지막으로 공인인증서의 공개키와 개인키를 가지고 어제 해본 전자서명을 한번 해보자.
package test.security; import java.io.ByteArrayOutputStream; import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.security.Key; import java.security.KeyFactory; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; import java.security.PrivateKey; import java.security.PublicKey; import java.security.Signature; import java.security.cert.CertificateFactory; import java.security.cert.X509Certificate; import java.security.interfaces.RSAPrivateCrtKey; import java.security.spec.PKCS8EncodedKeySpec; import javax.crypto.Cipher; import javax.crypto.EncryptedPrivateKeyInfo; import javax.crypto.spec.IvParameterSpec; import javax.crypto.spec.SecretKeySpec; import kr.kangwoo.util.ByteUtils; import org.bouncycastle.jce.provider.BouncyCastleProvider; import com.jarusoft.util.security.SHA1Utils; public class CertificateTest { public static void main(String[] args) throws Exception { String msg = "하늘에는 달이 없고, 땅에는 바람이 없습니다.\n사람들은 소리가 없고, 나는 마음이 없습니다.\n\n우주는 죽음인가요.\n인생은 잠인가요."; PublicKey publicKey = getPublicKey("C:/signCert.der"); PrivateKey privateKey = getPrivateKey("C:/signPri.key"); // 전자서명하기 Signature signatureA = Signature.getInstance("SHA1withRSA"); signatureA.initSign(privateKey); signatureA.update(msg.getBytes()); byte[] sign = signatureA.sign(); System.out.println("signature : " + ByteUtils.toHexString(sign)); // 전사서명 검증하기 String msgB = msg; Signature signatureB = Signature.getInstance("SHA1withRSA"); signatureB.initVerify(publicKey); signatureB.update(msgB.getBytes()); boolean verifty = signatureB.verify(sign); System.out.println("검증 결과 : " + verifty); } public static PublicKey getPublicKey(String file) throws Exception { X509Certificate cert = null; FileInputStream fis = null; try { fis = new FileInputStream(new File(file)); CertificateFactory certificateFactory = CertificateFactory.getInstance("X509"); cert = (X509Certificate) certificateFactory.generateCertificate(fis); } finally { if (fis != null) try {fis.close();} catch(IOException ie) {} } System.out.println(cert.getPublicKey()); return cert.getPublicKey(); } public static PrivateKey getPrivateKey(String file) throws Exception { // 1. 개인키 파일 읽어오기 byte[] encodedKey = null; FileInputStream fis = null; ByteArrayOutputStream bos = null; try { fis = new FileInputStream(new File(file)); bos = new ByteArrayOutputStream(); byte[] buffer = new byte[1024]; int read = -1; while ((read = fis.read(buffer)) != -1) { bos.write(buffer, 0, read); } encodedKey = bos.toByteArray(); } finally { if (bos != null) try {bos.close();} catch(IOException ie) {} if (fis != null) try {fis.close();} catch(IOException ie) {} } System.out.println("EncodedKey : " + ByteUtils.toHexString(encodedKey)); // 2. 개인카 파일 분석하기 EncryptedPrivateKeyInfo encryptedPrivateKeyInfo = new EncryptedPrivateKeyInfo(encodedKey); System.out.println(encryptedPrivateKeyInfo); System.out.println(encryptedPrivateKeyInfo.getAlgName()); byte[] salt = new byte[8]; System.arraycopy(encodedKey, 20, salt, 0, 8); System.out.println("salt : " + ByteUtils.toHexString(salt)); byte[] cBytes = new byte[4]; System.arraycopy(encodedKey, 30, cBytes, 2, 2); int iterationCount = ByteUtils.toInt(cBytes); System.out.println("iterationCount : " + ByteUtils.toHexString(cBytes)); System.out.println("iterationCount : " + iterationCount); String password = "password"; // 추출키(DK) 생성 byte[] dk = pbkdf1(password, salt, iterationCount); System.out.println("dk : " + ByteUtils.toHexString(dk)); // 생성된 추출키(DK)에서 처음 16바이트를 암호화 키(K)로 정의한다. byte[] keyData = new byte[16]; System.arraycopy(dk, 0, keyData, 0, 16); // 추출키(DK)에서 암호화 키(K)를 제외한 나머지 4바이트를 SHA-1 // 으로 해쉬하여 20바이트의 값(DIV)을 생성하고, 그 중 처음 16바이트를 초기 // 벡터(IV)로 정의한다. byte[] div = new byte[20]; byte[] tmp4Bytes = new byte[4]; System.arraycopy(dk, 16, tmp4Bytes, 0, 4); div = SHA1Utils.getHash(tmp4Bytes); System.out.println("div : " + ByteUtils.toHexString(div)); byte[] iv = new byte[16]; System.arraycopy(div, 0, iv, 0, 16); System.out.println("iv : " + ByteUtils.toHexString(iv)); // 3. SEED로 복호화하기 BouncyCastleProvider provider = new BouncyCastleProvider(); Cipher cipher = Cipher.getInstance("SEED/CBC/PKCS5Padding", provider); Key key = new SecretKeySpec(keyData, "SEED"); cipher.init(Cipher.DECRYPT_MODE, key, new IvParameterSpec(iv)); byte[] output = cipher.doFinal(encryptedPrivateKeyInfo.getEncryptedData()); PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(output); KeyFactory keyFactory = KeyFactory.getInstance("RSA"); RSAPrivateCrtKey privateKey = (RSAPrivateCrtKey)keyFactory.generatePrivate(keySpec); System.out.println(privateKey); return privateKey; } public static byte[] pbkdf1(String password, byte[] salt, int iterationCount) throws NoSuchAlgorithmException { byte[] dk = new byte[20]; // 생성이 의미가 없지만 한눈에 알아보라고 20바이트로 초기화 MessageDigest md = MessageDigest.getInstance("SHA1"); md.update(password.getBytes()); md.update(salt); dk = md.digest(); for (int i = 1; i < iterationCount; i++) { dk = md.digest(dk); } return dk; } }
#########################################################################################################
# 1. 파일명 : Common.java #
# 2. 용 도 : 한국어 웹 싸이트 개발시에 사용되는 JAVA용 배치 유틸리티 #
# 3. 개발자 : (주)이엔아이스튜디오 웹 사업팀 팀장 조동희 #
# 4. 싸이트 : http://blog.naver.com/dongheejo #
# 5. 최초 개발일 : 2009년 05월 13일 #
# 6. 최종 수정일 : 2009년 05월 13일 #
#########################################################################################################
/************************************************************************************************
* Common.java : 365일 24시간 동작하면서, 관련 웹하드 업체에 대한 배치 포인트를 실시한다. *
*************************************************************************************************/
// 관련 파일을 인쿨루드 한다.
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.MalformedURLException;
import java.net.Socket;
import java.net.URL;
import java.net.URLConnection;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;
import java.util.Locale;
// 클래스를 정의한다.
public class Common
{
// 메인메서드 정의
public static void main(String[] args)
{
Common ssb = new Common();
int i = 0;
while (true)
{
i++;
try
{
Thread.sleep(60000); // 최초 실행시간을 기점으로 60초 단위로 시간을 찍는다.
//포맷을 지정해서 날짜 구하기
SimpleDateFormat formatter =
new SimpleDateFormat("yyyy.MM.dd HH:mm:ss EEE",Locale.ENGLISH);
Date currentTime = new Date();
String dTime = formatter.format(currentTime);
System.out.println(dTime);
//System.out.println(dTime.indexOf(" 00:00"));
/********************************************************
* 일배치를 실행시킬 시간 (월요일 ~ 토요일 11:59) *
*********************************************************/
if(dTime.indexOf(" 23:59") != -1 && dTime.indexOf(" Sun")== -1)
{
System.out.println("Step-1. www.kiniwini.com Connect Resquest. : 명령어.");
ssb.callBatch("명령어");
}
/********************************************************
* 일배치를 실행 및 초기화할 시간 (일요일 11:59) *
*********************************************************/
if(dTime.indexOf(" 23:59") != -1 && dTime.indexOf(" Sun")!= -1)
{
System.out.println("Step-1. www.kiniwini.com Connect Resquest. : 명령어.");
ssb.callBatch("명령어");
System.out.println("Step-1. www.kiniwini.com Connect Resquest. : 명령어.");
ssb.callBatch("명령어");
}
}
catch (Exception e)
{
e.printStackTrace();
}
}
}
// 배치 함수 정의
public void callBatch(String CMD_COMMON)
{
// 속성 정의
URL url;// URL 주소 객체
URLConnection connection;// URL접속을 가지는 객체
InputStream is;// URL접속에서 내용을 읽기위한 Stream
InputStreamReader isr;
BufferedReader br;
String cmd_common = CMD_COMMON;
try
{
// URL객체를 생성하고 해당 URL로 접속한다..
url = new URL("접속주소);
connection = url.openConnection();
// 내용을 읽어오기위한 InputStream객체를 생성한다..
is = connection.getInputStream();
isr = new InputStreamReader(is);
br = new BufferedReader(isr);
// 내용을 읽어서 화면에 출력한다..
String buf = null;
while (true)
{
buf = br.readLine();
if (buf == null) break;
System.out.println("Step-2. www.kiniwini.com Connect Response. : " + buf);
}
}
catch (MalformedURLException mue)
{
System.err
.println("잘못되 URL입니다. 사용법 : java URLConn http://hostname/path]");
System.exit(1);
}
catch (IOException ioe)
{
System.err.println("IOException " + ioe);
ioe.printStackTrace();
System.exit(1);
}
}
}
[출처] java, 자바로 짠 배치 프로그램 ^^|작성자 사색의 즐거움
먼저 CACTUS와 StrutsTest를 받아야 한다.
CACTUS를 사용하기 위해서 해주어야 하는일은 3가지다.
1. class-path에 CACTUS 라이브러리들을 복사
2. web.xml 에 ServletRedirector Servlet 추가
<servlet>
<servlet-name>ServletRedirector</servlet-name>
<servlet-class>org.apache.cactus.server.ServletTestRedirector</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>ServletRedirector</servlet-name>
<url-pattern>/ServletRedirector</url-pattern>
</servlet-mapping>
cactus.contextURL=http://IP:PORT/CONTEXT ROOT
cactus.servletRedirectorName=ServletRedirector
cactus.enableLogging=true
이렇게 하면 CACTUS 사용 준비는 끝나게 되고 StrutsTest는 다운받은 jar파일을 lib에 추가만 해주면 된다.
그럼 TestCase를 작성해 보자
public class LoginTestCase extends CactusStrutsTestCase {
public LoginTestCase(String testName) {
super(testName);
}
/**
* Client side execution
*/
public void beginLogin(WebRequest theRequest) {
// Request Parameter Setting
theRequest.addParameter("KEY", "VALUE");
}
/**
* Server side execution
*/
public void testLogin() {
setRequestPathInfo("/LoginAction");
actionPerform();
verifyForward("login_success");
}
}
TestCase를 만들고 나면 WebAPP WEB-INF/classes에 추가해준다.
다른 방법이 있는지 모르겠지만 웹어플에 추가해주지 않으면 테스트가 안되었다.
3.0의 내용이지만 3.1만에서도 같다.
물론 4.0은 다르다
목차
1. 설치 및 설명
2. getMethod
3. postMethod
4. Header 설정
5. Cookie 설정
기타. 1) Reference
#### 1. 설명 및 설치
1-1. HttpClient 소개
HttpClient은 HTTP상에서 커뮤니케이션을 하는 자바 기반의 어플리케이션 개발을 쉽게 할수 있도록 제공한다.
우리가 웹 브라우저 또는 그에 준하는 어플리케이션을 개발한다면 HttpClient은 우리에게 클라이언트 코드 개발에 도움을 줄수있다.
이름에서 의미하는것과 같이 HttpClient는 오직 HTTP 클라이언트 코드을 위한 컴포넌트이지 HTTP 요청을 처리하는 서버측 프로세스을 지원하지는 않는다.
1-2. 설치
현재 아파치 HttpClient 는 3.0.1 안정버전을 지원한다.
Jakarta Commons HttpClient 페이지에서 다운로드 받으면 된다.
(다운로드 페이지: http://jakarta.apache.org/commons/httpclient/downloads.html)
(최신버전 다운로드:
- http://jakarta.apache.org/site/downloads/downloads_commons-httpclient.cgi
- http://mirror.apache-kr.org/jakarta/commons/httpclient/binary/commons-httpclient-3.0.1.zip
)
commons-httpclient-3.0.1.zip 를 받아서 압축을 풀고,
commons-httpclient-3.0.1.jar 를 CLASSPATH 에 추가하면 된다.
1-3. 추가 설정(Dependencies)
http://jakarta.apache.org/commons/httpclient/dependencies.html
Artifact ID Type Version Scope URL Comment
commons-codec jar 1.2 http://jakarta.apache.org/commons/codec/
http://jakarta.apache.org/site/downloads/downloads_commons-codec.cgi
commons-logging jar 1.0.4 http://jakarta.apache.org/commons/logging/
junit jar 3.8.1 test http://www.junit.org/
#### 2. getMethod 사용 예제
=========================== GetSample.java =================================
package test.httpclient;
import java.io.FileOutputStream;
import org.apache.commons.httpclient.HttpClient;
import org.apache.commons.httpclient.HttpMethod;
import org.apache.commons.httpclient.HttpStatus;
import org.apache.commons.httpclient.NameValuePair;
import org.apache.commons.httpclient.methods.GetMethod;
// Get Method를 사용한 예제
public class GetSample {
// 가져올 페이지 주소, 네이버 뉴스 속보
private static String url =
"http://news.naver.com/news/list.php";
// 파일에 byte[] 저장
public static void saveBytes(String filepath, byte[] byteData) throws Exception
{
FileOutputStream foStream = null;
try {
foStream = new FileOutputStream(filepath);
foStream.write(byteData);
} finally {
try { foStream.close(); foStream = null; } catch (Exception e) {}
}
}
/**
* @param args
*/
public static void main(String[] args) {
// HttpClient 생성
HttpClient client = new HttpClient();
// 요청 Method 지정
HttpMethod method = new GetMethod(url);
try {
// QueryString 지정, 1.문자열
url = url + "?mode=LSD§ion_id=001&menu_id=001&view=1";
// QueryString 지정, 2.NameValuePair 사용
// NameValuePair nvp1= new NameValuePair("mode","LSD");
// NameValuePair nvp2= new NameValuePair("section_id","001");
// NameValuePair nvp3= new NameValuePair("menu_id","001");
// NameValuePair nvp4= new NameValuePair("view","1");
// method.setQueryString(new NameValuePair[]{nvp1,nvp2, nvp3, nvp4});
System.out.println("QueryString>>> "+method.getQueryString());
// HTTP 요청 및 요청 결과
int statusCode = client.executeMethod(method);
// 요청 결과..
if (statusCode == HttpStatus.SC_OK) {
System.out.println("요청 성공");
System.out.println("응답 HTML:\n" + method.getResponseBodyAsString());
// 결과 저장
GetSample.saveBytes("naver_news.html", method.getResponseBody());
} else if ((statusCode == HttpStatus.SC_MOVED_TEMPORARILY) ||
(statusCode == HttpStatus.SC_MOVED_PERMANENTLY) ||
(statusCode == HttpStatus.SC_SEE_OTHER) ||
(statusCode == HttpStatus.SC_TEMPORARY_REDIRECT)) {
System.out.println("Redirecte !!!");
System.out.println("Redirect target: " + method.getResponseHeader("location").getValue());
}
} catch (Exception e) {
e.printStackTrace();
// Exception in thread "main" java.lang.NoClassDefFoundError: org/apache/commons/logging/LogFactory
// commons-logging.jar, commons-logging-api.jar 를 CLASSPATH에 추가한다.
// Exception in thread "main" java.lang.NoClassDefFoundError: org/apache/commons/codec/DecoderException
// commons-codec-1.3.jar 를 CLASSPATH에 추가한다.
}
}
}
=========================== GetSample.java =================================
#### 3. postMethod 사용
GetMethod 나 PostMethod 나 사용방법은 비슷합니다.
PostMethod method = new PostMethod("http://xx.com/ex_login_ok.php");
NameValuePair userid = new NameValuePair("nick", "aaaaa");
NameValuePair userpw = new NameValuePair("password", "bbbb");
method.setRequestBody( new NameValuePair[] {userid, userpw});
client.executeMethod(method);
#### 4. Header 사용
헤더를 사용하기 위해서는 setRequestHeader() 를 이용하여 원하는 헤더를 정의해 주시면 됩니다.
String charset = null; // 기존 charset 사용
//String charset = "euc_kr"; // charset 지정
// 요청 헤더 설정
if (charset == null) {
method.setRequestHeader("Content-Type", "application/x-www-form-urlencoded;");
} else {
method.setRequestHeader("Content-Type", "application/x-www-form-urlencoded; charset=" + charset);
}
#### 5. Cookie 사용
아직 정리가 않되서.. 분량이 좀 많네요.^^;
찬찬히 정리하겠습니다.
#### Reference
1) 자카르타 프로젝트 HttpClient와 FileUpload 사용하기
- http://blog.empas.com/inter999/read.html?a=3271313&l=1&v=trackback
import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
public class CommentClean {
/**
* @param args
* @throws IOException
*/
public static void main(String[] args) throws IOException {
StringBuffer sb = new StringBuffer();
File newFile = new File( "파일절대경로");
if (newFile.exists()) {
FileReader fr = new FileReader(newFile);
BufferedReader br = new BufferedReader(fr);
String readData = br.readLine();
while (readData != null) {
sb.append(readData);
readData = br.readLine();
}
int start = 0, end=0;
while(true){
start = sb.indexOf("/**");
end = sb.indexOf("*/");
// System.out.println("start : " + start);
// System.out.println("end : " + end);
if (start == -1 || end == -1 )
break;
sb.replace(start ,end+2, "");
start =0;
end = 0;
}
System.out.println(sb.toString().replaceAll("}", "}\n").replaceAll(";", ";\n").replaceAll("\\{", "\\{\n"));
br.close();
fr.close();
} else {
System.out.println("파일이 업네여....");
}
}
}