egovframework의 EgovHttpSessionBindingListener 클래스와 EgovMultiLoginPreventor 클래스로 중복 로그인 방지를 구현하던 중,
java.lang.illegalargumentexception setattribute non-serializable attribute ***에러가 발생했다.

session에 저장하기 위해선 해당 클래스 Serializable 클래스를 상속해 줘야 하는데, VO는 이미 상속이 되어 있었다.

VO 말고도 EgovHttpSessionBindingListener, EgovMultiLoginPreventor역시 Serializable 클래스를 상속해줘야 한다.

egov도 버젼 코드에 실수가 존재하나보다.

 

EgovHttpSessionBindingListener.java

package egovframework.com.utl.slm;

import java.io.Serializable;
import javax.servlet.http.HttpSessionBindingEvent;
import javax.servlet.http.HttpSessionBindingListener;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * @Class Name : EgovHttpSessionBindingListener.java
 * @Description : 중복 로그인 방지를 위해 사용자의 로그인 아이디와 세션을 제어하는 구현 클래스
 * @Modification Information
 *
 *               수정일 수정자 수정내용 ------- ------- ------------------- 2014.09.30 표준프레임워크 최초생성
 * @author YJ Kwon
 * @since 2014.09.30
 * @version 3.5
 */
public class EgovHttpSessionBindingListener implements HttpSessionBindingListener, Serializable {

  private static final long serialVersionUID = -6535453381287200501L;
  private static Logger logger = LoggerFactory.getLogger(EgovMultiLoginPreventor.class);

  /**
   * 사용자의 로그인 세션에 EgovHttpSessionBindingListener가 바인딩될 때 자동 호출되는 메소드로, 사용자 세션이 이미 존재하는지를 검사하여 하나의
   * 어플리케이션 내에서 하나의 세션만 유지되도록 한다
   */
  public void valueBound(HttpSessionBindingEvent event) {
    logger.error("\n\t!! VALUE BOUND !!");
    if (EgovMultiLoginPreventor.findByLoginId(event.getName())) {
      EgovMultiLoginPreventor.invalidateByLoginId(event.getName());
    }

    EgovMultiLoginPreventor.loginUsers.put(event.getName(), event.getSession());
  }


  /**
   * 
   * 로그아웃 혹은 세션타임아웃 설정에 따라 사용자 세션으로부터 EgovHttpSessionBindingListener가 제거될 때 자동 호출되는 메소드로, 사용자의 로그인
   * 아이디에 해당하는 세션을 ConcurrentHashMap에서 모두 제거한다
   */
  @Override
  public void valueUnbound(HttpSessionBindingEvent event) {
    logger.error("\n\t!! VALUE UNBOUND !!");
    EgovMultiLoginPreventor.loginUsers.remove(event.getName(), event.getSession());
  }

}

 

 

EgovMultiLoginPreventor.java

package egovframework.com.utl.slm;

import java.io.Serializable;
import java.util.Enumeration;
import java.util.concurrent.ConcurrentHashMap;

import javax.servlet.http.HttpSession;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * @Class Name : EgovMultiLoginPreventor.java
 * @Description : 중복 로그인 방지를 위해 사용자의 로그인 아이디와 세션 아이디를 관리하는 구현 클래스
 * @Modification Information
 *
 *               수정일 수정자 수정내용 ------- ------- ------------------- 2014.09.30 표준프레임워크 최초생성
 * @author YJ Kwon
 * @since 2014.09.30
 * @version 3.5
 */
public class EgovMultiLoginPreventor implements Serializable {

  private static final long serialVersionUID = 4012190701164146206L;

  private static Logger logger = LoggerFactory.getLogger(EgovMultiLoginPreventor.class);

  public static ConcurrentHashMap<String, HttpSession> loginUsers =
      new ConcurrentHashMap<String, HttpSession>();

  /**
   * 사용자의 로그인 아이디로 생성된 세션이 있는지를 확인한다
   */
  public static boolean findByLoginId(String loginId) {
    logger.error("\n\t>> loginUsers id : {} <<", loginId);
    return loginUsers.containsKey(loginId);
  }


  /**
   * 사용자의 로그인 아이디로 이미 존재하는 세션을 무효화한다
   */
  public static void invalidateByLoginId(String loginId) {
    Enumeration<String> e = loginUsers.keys();

    while (e.hasMoreElements()) {
      String key = (String) e.nextElement();

      logger.error("\n\t>> session key : {} <<", e.toString());
      if (key.equals(loginId)) {
        logger.error("\n\t:: SESSION INVALIDATE {} <<", loginId);
        loginUsers.get(key).invalidate();
      }
    }
  }

}
728x90
반응형

pom.xml

pom.xml에 다음과 같은 Dependency를 추가 해 준다.
버전에 따라 지원이 되지 않을 수 있다.

<!-- JSON을 위한 Dependency -->
<dependency>
  <groupId>net.sf.json-lib</groupId>
  <artifactId>json-lib</artifactId>
  <version>2.4</version>
  <classifier>jdk15</classifier>
</dependency>
<dependency>
  <groupId>com.fasterxml.jackson.core</groupId>
  <artifactId>jackson-databind</artifactId>
  <version>2.9.5</version>
</dependency>
<dependency>
  <groupId>org.codehaus.jackson</groupId>
  <artifactId>jackson-mapper-asl</artifactId>
  <version>1.9.13</version>
</dependency>
<dependency>
  <groupId>org.codehaus.jackson</groupId>
  <artifactId>jackson-mapper-asl</artifactId>
  <version>1.9.13</version>
</dependency>
<!-- JSON을 위한 Dependency -->

servlet.xml

servlet.xml 혹은 dispatcher-servlet.xml

<mvc:annotation-driven>
  <mvc:message-converters>
  
  <!-- 
    이 부분은 Controller에서 일반적인 HTML을 리턴하기 위한 설정이다.
    JSON을 리턴하지 않을 경우는 Default 값으로 지정 되어 있기 때문에 설정 할 필요 없지만,
    JSON 리턴과 HTML 리턴을 모두 하려면은 명시적으로 설정 해 줘야 한다.
   -->
    <bean class="org.springframework.http.converter.StringHttpMessageConverter">
      <property name="supportedMediaTypes">
        <list>
          <value>text/html; charset=UTF-8</value>
        </list>
      </property>
    </bean>
    
    <!--
      Controller에서 JSON 리턴시 객체를 변환 해주기 위해서 MessageConverter가 필요하다. 
    -->
    <bean class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter">
      <property name="supportedMediaTypes">
        <list>
          <value>application/json; charset=UTF-8</value>
        </list>
      </property>
    </bean>
    
  </mvc:message-converters>
</mvc:annotation-driven>

Controller

Controller는
필수적으로 @ResponceBody
권장 사항으로 produces = "application/json; charset=UTF-8" 를 명시 해 준다.
produces 속성은 Response의 Content-Type을 제어한다.

@RequestMapping(
    value = "/reqWaterDepth.do",
    method = RequestMethod.POST,
    produces = "application/json; charset=UTF-8")
@ResponseBody
public Map<String, List<WaterDepthDTO>> reqWaterDepth (
    HttpServletRequest req,
    HttpServletResponse res,
    @RequestParam Map<String, String> area
      ) throws Exception {
  logger.info("\n\tREQ Water Depth . do \n" + area.get("area") + "\n");
  return service.getWaterDepthInRange(area.get("area"));
}

Ajax Request

Request 부분에서는 딱히 설정할 것이 없다.

function getData(area) {
  return new Promise(function(resolve, reject) {
    console.group('drawSurvArea getData');
    area = {
      'area' : area
    };
    //  console.log( area );
    $.ajax({
      type : "POST",
      url : "/reqWaterDepth.do",
      data : area,
      dataType : 'json',
      beforeSend : function(xhr, opts) {
        console.log("before send");
        // when validation is false
        if (false) {
          xhr.abort();
        }
      },
      success : function(res) {
        console.log("SUCCESS");
        console.log(res);
        if (Object.keys(res).length === 0
            && JSON.stringify(res) === JSON.stringify({})) {
          alert('해당 영역에 수심데이터가 없습니다.');
          return;
        } else {
          resolve(res);
        }
      },
      error : function(err) {
        console.log("ERR");
        console.error(err.statusText);
        alert('데이터를 처리 하던중 에러가 발생했습니다.\n' + err.statusText + ' : ' + err.status);
        reject(err);
      }
    });
    console.groupEnd('drawSurvArea getData');
  })
}
getData(area).then(function(data) {
  console.log('THEN')
  console.log(data);
});
728x90
반응형

+ Recent posts