'spring'에 해당되는 글 2건

  1. 2012.03.18 spring mvc 3 code 관리 (enum) by 파이팅야 4
  2. 2012.03.08 spring mvc 3 error log by 파이팅야 4
code 관리를 enum으로 할 때 해당 코드들을 웹 화면에 comboBox, radiobutton, checkbox 등등으로 보여줄때 다국어 지원이 안된다. (<form:select> 안에 resourceBundle과 연동되어 처리되는 attribute가 있었으면 좋을련만...) 그럴때 다음과 같이 하면 어떨지... 간단하게 만들어봄.... refactoring은 더 해야 하지만...(DB에는 short type의 column에 숫자 코드값만 들어가는 경우임, DB의 table에 코드성 데이터가 있더라도 동적으로 추가 가능한 기능(페이지)가 없다면 enum으로 관리 할 수도 있을것 같다.


spring context에

<bean id="messageSource"
class="org.springframework.context.support.ReloadableResourceBundleMessageSource">
<property name="basenames">
<list>
<value>classpath:/message/validation</value>
<value>classpath:/message/label</value>
</list>
</property>
       <property name="defaultEncoding" value="UTF-8" />
       <property name="cacheSeconds">
           <value>60</value>
       </property>
</bean>

label_ko.properties 에 다음과 같이 WriterTypeCode에 대한 label명을 넣고
label.WriterTypeCode.CUSTOMER = \uACE0\uAC1D
label.WriterTypeCode.MANAGER  = \uAD00\uB9AC\uC790
label.WriterTypeCode.SYSTEM   = \uC2DC\uC2A4\uD15C

Code interface를 만들고
public interface ICode {
short getCode();
}


사용할 Code enum을 만들고
public enum WriterTypeCode implements ICode {
CUSTOMER((short)1),
MANAGER((short)2),
SYSTEM((short)3);
private short code;
private String codeLabel;
private WriterTypeCode(short code) {
this.code = code;
}
@Override
public short getCode() {
return code;
}
public void setCode(short code) {
this.code = code;
}
public String getCodeLabel() {
return codeLabel;
}
public void setCodeLabel(String codeLabel) {
this.codeLabel = codeLabel;
}
}

controller에서 modelAttribute를 만들고
@Controller
@RequestMapping("/code/*")
public class CodeEnumController {

@Autowired
private MessageSource messageSource;

@ModelAttribute("writerTypeCodes")
public EnumSet<WriterTypeCode> writerTypeCodes() {
EnumSet<WriterTypeCode> writerTypeCodes = EnumSet.allOf(WriterTypeCode.class);
String message = null;
for(WriterTypeCode writerTypeCode : writerTypeCodes) {
message = this.messageSource.getMessage(
"label.WriterTypeCode." + writerTypeCode.name(), null, Locale.getDefault());
writerTypeCode.setCodeLabel(message);
}
return writerTypeCodes;
}

@RequestMapping(value="/code", method=RequestMethod.GET)
public ModelAndView code() {
CodeGroup codeGroup = new CodeGroup();
codeGroup.setName("testName");
codeGroup.setOrder(1);
codeGroup.setWriterTypeCode(WriterTypeCode.MANAGER);
ModelAndView mav = new ModelAndView();
mav.setViewName("code/code");
mav.addObject("codeGroup", codeGroup);
return mav;   



jsp에서 보여준다.
<form:select path="writerTypeCode" items="${writerTypeCodes}" itemLabel="codeLabel" itemValue="code"></form:select> 

<spring:message code="label.WriterTypeCode.${communication.writerTypeCode}" 
var="writerTypeCodeValue" />
writerTypeCode [${writerTypeCodeValue}]


post 방식으로 Model에 binding될때는 Controller에 다음의 코드를 넣고
@InitBinder
public void initBinder(WebDataBinder binder) {
    binder.registerCustomEditor(WriterTypeCode.class, new CodePropertyEditor(WriterTypeCode.class));
}

@RequestMapping(value="/code", method=RequestMethod.POST)
public ModelAndView codePost(@ModelAttribute CodeGroup codeGroup ) {
System.out.println("################## codeGroupResult[" + codeGroup + "]");
ModelAndView mav = new ModelAndView();
mav.setViewName("code/code");
mav.addObject("codeGroup", codeGroup);
return mav;   
}

CodePropertyEditor 를 만든다.
public class CodePropertyEditor extends PropertyEditorSupport {
@SuppressWarnings("rawtypes")
Class clazz; 

@SuppressWarnings("rawtypes")
public CodePropertyEditor(Class clazz) {
this.clazz = clazz;
}

@Override
public void setAsText(String text) throws IllegalArgumentException {
if (!StringUtils.hasLength(text)) {
super.setValue(null);
throw new IllegalArgumentException();
}
if (!text.matches("\\d+")) {
super.setValue(null);
throw new IllegalArgumentException();
}
if (!ICode.class.isAssignableFrom(this.clazz)) {
super.setValue(null);
throw new IllegalArgumentException();
}
@SuppressWarnings({ "rawtypes", "unchecked" })
EnumSet typeCodes = EnumSet.allOf(this.clazz);
int code = -1;
int parameterCode = Integer.valueOf(text);
for(Object typeCode : typeCodes) {
code = ((ICode)typeCode).getCode(); 
if (code == parameterCode) {
super.setValue(typeCode);
break ;
}
}
}

여기까지 하면 웹에서의 처리는 끝나고 DB쪽에 binding하기 위해서는 myBatis인 경우 다음과 같이 TypeHandler를 만들어서 연동 하면 된다.  다음과 같이 ACodeTypeHandler를 만들고 
public abstract class ACodeTypeHandler<E extends Enum<E>> implements TypeHandler<E> {
//** log와 validation 체크 해야 함
private EnumSet<E> enumSet;

public ACodeTypeHandler(Class<E> enumClass){
if(!ICode.class.isAssignableFrom(enumClass))
throw new IllegalArgumentException(); // 메세지 추가하기
this.enumSet = EnumSet.allOf(enumClass);
}
@Override
public E getResult(ResultSet resultSet, String columnName)
throws SQLException {
if (resultSet.wasNull())
return null;
return valueOf(resultSet.getShort(columnName));
}

@Override
public E getResult(ResultSet resultSet, int columnIndex)
throws SQLException {
if (resultSet.wasNull())
return null;
return valueOf(resultSet.getShort(columnIndex));
}

@Override
public E getResult(CallableStatement callableStatement, int columnIndex)
throws SQLException {
if (callableStatement.wasNull())
return null;
return valueOf(callableStatement.getShort(columnIndex));
}

@Override
public void setParameter(PreparedStatement preparedStatement, int parameterIndex, 
E parameter, JdbcType jdbcType) throws SQLException {
     if (parameter == null)
     preparedStatement.setNull(parameterIndex, Type.SHORT);
     else
     preparedStatement.setShort(parameterIndex, ((ICode)parameter).getCode());
}
public E valueOf(short value) {
for(E e: this.enumSet){
if((((ICode)e)).getCode() == value)
return e;
}
return null;
}
}

WriterTypeCodeTypeHandler 를 만들고
public class WriterTypeCodeTypeHandler extends ACodeTypeHandler<WriterTypeCode> {
public WriterTypeCodeTypeHandler() {
super(WriterTypeCode.class);
}
}

mybatis config 파일에 다음의 내용을 넣으면 preparedStatement로 값셑팅하기 전과 ResultSet으로 결과값 Model에 binding 할때 자동으로 변환 된다.
<configuration>
<typeHandlers>
<typeHandler javaType="net.board.type.WriterTypeCode" handler="net.board.type.handler.WriterTypeCodeTypeHandler" />
</typeHandlers>
</configuration>


모든 Controller에서 사용할 수 있도록 AnnotationMethodHandlerAdapter.webBindingInitializer에 등록해도 되고... 이렇게 하면 enum으로 코드 관리가 되지 않을지... 

  --------------------------------------------------------------------------------------------------

mybatis와의 연동을 위해서는 ICode를 사용해야 하지만... 웹에서 comboBox나 listBox등에 enum의 내용을 출력하고 결과값을 해당 모델에 바인딩할때 ICode를 제거하고 spring:message와 연동해서 roof 돌면서 보여주고 '선택해 주세요'와 같은 것이 제일 상단에 보여지는 경우에는 결과값을 ''으로 해서 처리하면 '@ModelAttribute("writerTypeCodes")'의 소스코드와 initBinder()의 소스코드를 사용하지 않아도 된다. 예로 다음같이 view에서 enum값을 화면에 출력하고

<%@ taglib prefix="t" tagdir="/WEB-INF/tags" %>

<t:enumSelect path="modifyTypeCode" type="Header" ></t:enumSelect>


다음과 같은 tag 내용이면 modifyTypeCode용 @ModelAttribute가 필요없고 initBinder()에 소스추가도 필요없게 된다.

<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>

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

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

<%@ attribute name="path" type="java.lang.Object" rtexprvalue="true" required="true" %>

<%@ attribute name="type" type="java.lang.String" rtexprvalue="true" required="false" %>

<spring:eval expression="'${nestedPath}'.substring(0, '${nestedPath}'.length() - 1)" var="basePath"/>

<spring:eval expression="${basePath}" var="baseBean"/>

<spring:eval expression="new org.springframework.beans.BeanWrapperImpl(baseBean)" var="beanWrapper"/>

<spring:eval expression="beanWrapper.getPropertyType('${path}')" var="pathClass"/>

<!-- Create a select option for each of the enum constants. -->

<form:select path="${path}">

    <c:if test="${type == 'Header'}">

            <form:option value=""><spring:message code="${pathClass.simpleName}.Header"/></form:option>

    </c:if>

    <c:if test="${type == 'All'}">

            <form:option value=""><spring:message code="All"/></form:option>

    </c:if>

    <c:forEach items="${pathClass.enumConstants}" var="enumConst">

        <!-- Value of option is the enum constant name, which spring knows

         how to bind using enum.valueOf(). -->

        <form:option value="${enumConst}">

        <!-- Label is pulled from propfile as simpleName.constant, e.g Color.RED -->    

        <spring:message code="${pathClass.simpleName}.${enumConst}"/>

        </form:option>

    </c:forEach>

</form:select>

Posted by 파이팅야
,

spring mvc 3 error log

Spring 2012. 3. 8. 17:44
에러에 대해서 급하게 정리 해봄

 web.xml에 http error와 web container error에 대해서 다음과 같이 기술하고
<!-- errorPage 설정 -->
   <error-page>
      <exception-type>java.lang.Exception</exception-type>
      <location>/WEB-INF/view/error/http/exception.jsp</location>
   </error-page>
   <error-page>
      <error-code>404</error-code>
      <location>/WEB-INF/view/error/http/404.jsp</location>
    </error-page>

spring mvc 설정에 다음과 같이 exception 발생 시 exception.jsp파일이 호출되게 한 후
<!--  Exception 처리 -->
<bean class="org.springframework.web.servlet.handler.SimpleMappingExceptionResolver">
<property name="exceptionMappings">
<props>
<prop key="java.lang.Exception">/error/exception/exception</prop>
</props>
</property>
</bean>
 

해당 jsp 파일에 다음의 내용을 추가해서 로그를 찍으면 좀더 유지보수에 좋지 않을지...
<%@ page import="org.apache.log4j.Logger" %>
<%
Logger logger = Logger.getLogger("spring exception");

// client의 cookie나 request Uri 정보등등을 로그에 남기기
String newLine = System.getProperty("line.separator");
StringBuffer sb = new StringBuffer()
.append("requestURL[").append(request.getRequestURL()).append("]").append(newLine)
.append("pathInfo[").append(request.getPathInfo()).append("],").append(newLine)
.append("method[").append(request.getMethod()).append("],").append(newLine)
.append("queryString[").append(request.getQueryString()).append("],").append(newLine)
.append("remoteHost[").append(request.getRemoteHost()).append("],").append(newLine)
.append("remoteAddr[").append(request.getRemoteAddr()).append("],").append(newLine)
.append("servletPath[").append(request.getServletPath()).append("],").append(newLine);
    // http header값도 로그에 추가
java.util.Enumeration names = request.getHeaderNames();
    String headerName;
    while (names.hasMoreElements()) {
    headerName = (String)names.nextElement();
    sb.append(headerName).append("[").append(request.getHeader(headerName)).append("],").append(newLine);
    }
    sb.append("serverName[").append(request.getServerName()).append("]");
logger.error(sb.toString());
// Exception 정보도 로그에 추가
if (request.getAttribute("javax.servlet.error.exception") == null) {
logger.debug("request.getAttribute('javax.servlet.error.exception') is null");
return; // Return from the JSP servelet handler.
}
// javax.servlet.error.status_code: 에러 상태 코드를 말해 주는 정수이다.
// javax.servlet.error.exception_type: 에러가 생기게 된 예외 형을 지적해 주는 클래스 인스턴스이다. 
// javax.servlet.error.message: 예외 메시지를 말해주는 스트링이며, 예외 컨스트럭터로 보내어 진다.
// javax.servlet.error.exception: 실제 예외가 없어지면 버릴 수 있는 객체이다. 
// javax.servlet.error.request_uri: 문제를 일으킨 리소스의 URI를 말해주는 스트링이다.
Throwable throwable = (Throwable) request.getAttribute("javax.servlet.error.exception");
logger.error("spring exception", throwable);
%>

결과 화면
17:33:02,150 ERROR spring exception:101 - requestURL[http://localhost:8080/board2/WEB-INF/view/error/exception/exception.jsp]
pathInfo[null],
method[GET],
queryString[null],
remoteHost[0:0:0:0:0:0:0:1],
remoteAddr[0:0:0:0:0:0:0:1],
servletPath[/WEB-INF/view/error/exception/exception.jsp],
accept[*/*],
accept-language[ko-KR],
user-agent[Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 6.1; WOW64; Trident/4.0; SLCC2; .NET CLR 2.0.50727; .NET CLR 3.5.30729; .NET CLR 3.0.30729; Media Center PC 6.0; InfoPath.3; .NET CLR 1.1.4322; .NET4.0C; .NET4.0E)],
accept-encoding[gzip, deflate],
host[localhost:8080],
connection[Keep-Alive],
cookie[JSESSIONID=4218FE934DC460160394497F33DEE71E],
serverName[localhost] 

 
/WEB-INF/view/error/http/exception.jsp 파일에는 다음의 부분만 추가 및 수정하고...
response.setStatus(javax.servlet.http.HttpServletResponse.SC_OK);
Logger logger = Logger.getLogger("http exception"); 

 
asp.net mvc 에서는 다음과 같이 global.asax.cs에 넣고 ...
         protected void Application_Error(Object sender, EventArgs e)
        {
            if (Config.RedirectCustomErrorPage)
            {
                var exception = Server.GetLastError();
                var httpException = exception as HttpException;

                Response.Clear();
                Server.ClearError();

                var routeData = new RouteData();
                routeData.Values["controller"] = "Errors";
                routeData.Values["action"] = "ServerError";
                routeData.Values["exception"] = exception;

                Response.StatusCode = 500;

                if (httpException != null)
                {
                    /* 
                     * 100 Series : Informational 
                     * 200 Series : Success 
                     * 300 Series : Redirection
                     * 400 Series : Client Error
                     * 500 Series : Server Error
                     */
                    Response.StatusCode = httpException.GetHttpCode();
                    if (400 <= Response.StatusCode && Response.StatusCode < 500)
                        routeData.Values["action"] = "HttpError"; //400대 에러가 들어온다
                    else
                        routeData.Values["action"] = "ServerError"; // 500대 에러가 들어온다
                }

                IController errorController = new ErrorController();
                var rc = new RequestContext(new HttpContextWrapper(Context), routeData);
                errorController.Execute(rc);
            }
            else
            {
                System.Text.StringBuilder sbErrMsg = new System.Text.StringBuilder();
                sbErrMsg.Append("[URL : (");
                sbErrMsg.Append(Request.Url.ToString());
                sbErrMsg.Append(")][URLRefferrer : (");
                sbErrMsg.Append(Request.UrlReferrer.ToString());
                sbErrMsg.Append(")][PATH_INFO : (");
                sbErrMsg.Append(Request.ServerVariables["PATH_INFO"].ToString());
                sbErrMsg.Append(")][REQUEST_METHOD : (");
                sbErrMsg.Append(Request.ServerVariables["REQUEST_METHOD"].ToString());
                sbErrMsg.Append(")][QUERY_STRING : (");
                sbErrMsg.Append(Request.ServerVariables["QUERY_STRING"].ToString());
                sbErrMsg.Append(")][HTTP_COOKIE : (");
                sbErrMsg.Append(Request.ServerVariables["HTTP_COOKIE"].ToString());
                sbErrMsg.Append(")][UserHostName : (");
                sbErrMsg.Append(Request.UserHostName.ToString());
                sbErrMsg.Append(")][HTTP_USER_AGENT : (");
                sbErrMsg.Append(Request.ServerVariables["HTTP_USER_AGENT"].ToString());
                sbErrMsg.Append(")][HTTP_ACCEPT_LANGUAGE : (");
                sbErrMsg.Append(Request.ServerVariables["HTTP_ACCEPT_LANGUAGE"].ToString());
                sbErrMsg.Append(")][SERVER_NAME : (");
                sbErrMsg.Append(Request.ServerVariables["SERVER_NAME"].ToString());
                sbErrMsg.Append(")]");

                Exception exErr = Server.GetLastError();
                Logger.GetInstance().Log.Error("(Global.Application_Error) Application Error, ["
                    + sbErrMsg.ToString() + "]", exErr);
            }
        }
Posted by 파이팅야
,