아키텍처와 함께

블로그 이미지
by gregorio
  • Total hit
  • Today hit
  • Yesterday hit

'분류 전체보기'에 해당되는 글 56건

  1. 2018.03.16
    Spring Framework File Upload with Drag and Drop
  2. 2018.03.09
    Spring framework XML Marshaller( Jaxb2Marshaller )
  3. 2018.03.08
    Maven Oracle dependency
  4. 2018.03.02
    spring framework interceptor
  5. 2018.02.23
    APM Scouter 설치

프로젝트를 수행하면서 파일 업로드는 다양한 방식으로 개발이 가능하다.


파일 업로드 시 업로드할 파일을 Drag and Drop 방식으로 File을 브라우저에 첨부한 후 업로드하는 방식에 대해 설명하고자 한다.


인터넷을 검색하면 Drag and Drop의 다양한 파일 업로드 방식이 있으나, 적용 시 손쉽게 적용할 수 있는 방법을 소개한다.


먼저 파일을 업로드하는 Jsp파일이다.


■ fileUpload.jsp 전체 소스

<html>

<head>

<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.10.1/jquery.min.js"></script>

<style>

#dragandrophandler

{

border:2px dotted #0B85A1;

width:400px;

color:#92AAB0;

text-align:left;vertical-align:middle;

padding:10px 10px 10 10px;

margin-bottom:10px;

font-size:200%;

}



.progressBar {

    width: 200px;

    height: 22px;

    border: 1px solid #ddd;

    border-radius: 5px; 

    overflow: hidden;

    display:inline-block;

    margin:0px 10px 5px 5px;

    vertical-align:top;

}

 

.progressBar div {

    height: 100%;

    color: #fff;

    text-align: right;

    line-height: 22px; /* same as #progressBar height if we want text middle aligned */

    width: 0;

    background-color: #0ba1b5; border-radius: 3px; 

}

.statusbar

{

    border-top:1px solid #A9CCD1;

    min-height:25px;

    width:700px;

    padding:10px 10px 0px 10px;

    vertical-align:top;

}

.statusbar:nth-child(odd){

    background:#EBEFF0;

}

.filename

{

display:inline-block;

vertical-align:top;

width:250px;

}

.filesize

{

display:inline-block;

vertical-align:top;

color:#30693D;

width:100px;

margin-left:10px;

margin-right:5px;

}

.abort{

    background-color:#A8352F;

    -moz-border-radius:4px;

    -webkit-border-radius:4px;

    border-radius:4px;display:inline-block;

    color:#fff;

    font-family:arial;font-size:13px;font-weight:normal;

    padding:4px 15px;

    cursor:pointer;

    vertical-align:top

    }

</style>


<script>


var arrStatus = [];

var formDatas = [];

$(document).ready(function()

{

var obj = $("#dragandrophandler");

obj.on('dragenter', function (e) 

{

    e.stopPropagation();

    e.preventDefault();

    $(this).css('border', '2px solid #0B85A1');

});

obj.on('dragover', function (e) 

{

     e.stopPropagation();

     e.preventDefault();

});

obj.on('drop', function (e) 

{

 

     $(this).css('border', '2px dotted #0B85A1');

     e.preventDefault();

     var files = e.originalEvent.dataTransfer.files;

 

     //We need to send dropped files to Server

     handleFileUpload(files,obj);

});

$(document).on('dragenter', function (e) 

{

    e.stopPropagation();

    e.preventDefault();

});

$(document).on('dragover', function (e) 

{

  e.stopPropagation();

  e.preventDefault();

  obj.css('border', '2px dotted #0B85A1');

});

$(document).on('drop', function (e) 

{

    e.stopPropagation();

    e.preventDefault();

});

$("#upload").click(function(e){

e.preventDefault();

alert("send file to server");

for (var i = 0; i < arrStatus.length; i++) {

debugger;

sendFileToServer(formDatas[i], arrStatus[i]);

}

    });  

});

function sendFileToServer(formData,status)

{

    var uploadURL ="http://localhost:8080/sample/fileupload.do"; //Upload URL

    var extraData ={path:"d:/temp/files", }; //Extra Data.

    formData.append("path", "d:/temp/files");

    formData.append("saveName", "log_org.zip");

    

    debugger;

    var jqXHR=$.ajax({

            xhr: function() {

            var xhrobj = $.ajaxSettings.xhr();

            if (xhrobj.upload) {

                    xhrobj.upload.addEventListener('progress', function(event) {

                        var percent = 0;

                        var position = event.loaded || event.position;

                        var total = event.total;

                        if (event.lengthComputable) {

                            percent = Math.ceil(position / total * 100);

                        }

                        //Set progress

                        status.setProgress(percent);

                    }, false);

                }

            return xhrobj;

        },

    url: uploadURL,

    type: "POST",

    contentType:false,

    processData: false,

        cache: false,

        data: formData,

        success: function(data){

            status.setProgress(100);

 

            $("#status1").append("File upload Done<br>");         

        }

    }); 

 

    status.setAbort(jqXHR);

}

 

var rowCount=0;

function createStatusbar(obj)

{

     rowCount++;

     var row="odd";

     if(rowCount %2 ==0) row ="even";

     this.statusbar = $("<div class='statusbar "+row+"'></div>");

     this.filename = $("<div class='filename'></div>").appendTo(this.statusbar);

     this.size = $("<div class='filesize'></div>").appendTo(this.statusbar);

     this.progressBar = $("<div class='progressBar'><div></div></div>").appendTo(this.statusbar);

     this.abort = $("<div class='abort'>Abort</div>").appendTo(this.statusbar);

     obj.after(this.statusbar);

 

    this.setFileNameSize = function(name,size)

    {

        var sizeStr="";

        var sizeKB = size/1024;

        if(parseInt(sizeKB) > 1024)

        {

            var sizeMB = sizeKB/1024;

            sizeStr = sizeMB.toFixed(2)+" MB";

        }

        else

        {

            sizeStr = sizeKB.toFixed(2)+" KB";

        }

 

        this.filename.html(name);

        this.size.html(sizeStr);

    }

    this.setProgress = function(progress)

    {       

        var progressBarWidth =progress*this.progressBar.width()/ 100;  

        this.progressBar.find('div').animate({ width: progressBarWidth }, 10).html(progress + "% ");

        if(parseInt(progress) >= 100)

        {

            this.abort.hide();

        }

    }

    this.setAbort = function(jqxhr)

    {

        var sb = this.statusbar;

        this.abort.click(function()

        {

            jqxhr.abort();

            sb.hide();

        });

    }

}

function handleFileUpload(files,obj)

{

   for (var i = 0; i < files.length; i++) {

    //FormData

var fd = new FormData();

        fd.append('file', files[i]);

        formDatas[i] = fd;

 

        //Status bar

        var status = new createStatusbar(obj); //Using this we can set progress.

        status.setFileNameSize(files[i].name,files[i].size);

        arrStatus[i] = status;

        //sendFileToServer(fd,status);

 

   }

}


</script>

</head>

 

<body>

<div id="dragandrophandler">Drag & Drop Files Here</div>

<br><br>

<div>

<button type="button" id="upload" name="upload">upload</button>

</div>

<div id="status1"></div>


</body>

</html>


Drag and Drop으로 파일 업로드하는 두가지 방식이 있다.

인터넷을 검색하여 찾은 소스는 파일 선택 시 바로 업로드 하게 되어 있는데 변경한 방식은 UPLOAD 버튼을 선택 시 파일을 업로드 하게 재구성하였다.



Script의 handleFileUpload의 FormData와 status객체를 생성하여 sendFileToServer function을 바로 호출하면 파일을 Drag and Drop이 완료된 후 바로 파일을 업로드 한다.



■ Drag & Drop 시 서버로 파일 전송

  function handleFileUpload(files,obj)

{

   for (var i = 0; i < files.length; i++) {

var fd = new FormData();

        fd.append('file', files[i]);

 

        //Status bar

        var status = new createStatusbar(obj); //Using this we can set progress.

        status.setFileNameSize(files[i].name,files[i].size);

        sendFileToServer(fd,status);

 

   }

}



그러나 업무상으로 File을 Drag & Drop으로 첨부한 후 upload 버튼을 선택 시 파일을 서버로 전송하고자 하는 경우에는 Array Object를 생성하여 FormData와 status를 Array Object에 저장한 후 Upload 버튼 선택 시 파일을 업로드할 수 있다.


■ Drag & Drop 파일 객체 저장 방식

  function handleFileUpload(files,obj)

{

   for (var i = 0; i < files.length; i++) {

    //FormData

var fd = new FormData();

        fd.append('file', files[i]);

        formDatas[i] = fd;

 

        //Status bar

        var status = new createStatusbar(obj); //Using this we can set progress.

        status.setFileNameSize(files[i].name,files[i].size);

        arrStatus[i] = status;

 

   }

}


■ upload 버튼 Click 시 파일 전송

$("#upload").click(function(e){

e.preventDefault();

alert("send file to server");

for (var i = 0; i < arrStatus.length; i++) {

debugger;

sendFileToServer(formDatas[i], arrStatus[i]);

}

 


파일을 업로드 할 때 추가적인 정보를 같이 넘겨주는 경우가 있는데 파일을 저장하기 위해 FormData 객체를 사용하고 있기 때문에 FormData 의 append method를 이용하여 추가적인 정보를 같이 전달할 수 있다.


  function sendFileToServer(formData,status)

{

    var uploadURL ="http://localhost:8080/sample/fileupload.do"; //Upload URL

    var extraData ={path:"d:/temp/files", }; //Extra Data.

    formData.append("path", "d:/temp/files");

    formData.append("saveName", "log_org.zip");

    



다음은 서버 프로그램이다.


먼저 Fileupload 화면을 보여주는 Controller이다.


@RequestMapping(value="/sample/filePageView.do")

public ModelAndView filePageView() throws Exception {

ModelAndView mav = new ModelAndView();

mav.setViewName("fileUpload");

return mav;


ModelAndView 객체를 생성하여 fileUpload.jsp를 browser에 보여준다.


파일을 업로드하는 Controller이다.


@RequestMapping(value="/sample/fileupload.do", method = RequestMethod.POST)

    public @ResponseBody LinkedList<Map<String, Object>> upload(MultipartHttpServletRequest request, HttpServletResponse response,

    String path, String saveName) {

 

        //1. build an iterator

         Iterator<String> itr =  request.getFileNames();

         MultipartFile mpf = null;

 

         LinkedList<Map<String, Object>> files = new LinkedList<Map<String, Object>>();

         //2. get each file

         while(itr.hasNext()){

 

             //2.1 get next MultipartFile

             mpf = request.getFile(itr.next());

             LOGGER.debug(mpf.getOriginalFilename() +" uploaded! "+ files.size());

 

 

             //2.3 create new fileMeta

             Map<String, Object> fileMeta = new HashMap<String, Object>();

             fileMeta.put("fileName", mpf.getOriginalFilename());

             fileMeta.put("fileSize", mpf.getSize()/1024+" Kb");

             fileMeta.put("fileType", mpf.getContentType());

// 

             try {

//                fileMeta.setBytes(mpf.getBytes());

 

                 // copy file to local disk (make sure the path "e.g. D:/temp/files" exists)            

                 FileCopyUtils.copy(mpf.getBytes(), new FileOutputStream("D:/temp/files/"+mpf.getOriginalFilename()));

 

            } catch (IOException e) {

                // TODO Auto-generated catch block

                e.printStackTrace();

            }

             //2.4 add to files

             files.add(fileMeta);

         }

        // result will be like this

        // [{"fileName":"app_engine-85x77.png","fileSize":"8 Kb","fileType":"image/png"},...]

        return files;

    } 


Chrome과 IE 11버전에서 정상적으로 실행되는것을 확인하였다.




'Spring Framrwork' 카테고리의 다른 글

Spring framework Property Reload  (0) 2018.04.13
Mybatis include sql  (0) 2018.03.23
Spring framework XML Marshaller( Jaxb2Marshaller )  (0) 2018.03.09
spring framework interceptor  (0) 2018.03.02
Google Map javascript API 사용법  (0) 2018.02.07
AND

Spring framework에서 Jaxb2Marshaller를 사용하는 경우 관련된 classesToBeBound 또는 packagesToScan를 속성을 이용하여 XML 변환이 필요한 객체를 찾는다.


classToBeBound를 사용하는 경우 해당 Class를 주입해야하고, packageToScan을 사용하는 경우 해당 package를 여러개 선언해야 한다.


■ classToBeBound 와 packageToScan 설정 예

<bean id="jaxbMarshaller" class="org.springframework.oxm.jaxb.Jaxb2Marshaller">

    <property name="packagesToScan"-->

     <!-- set package name without * -->

    <list>

    <value>com.sample.vo</value>

    </list>

</property>

</bean>



<bean id="jaxbMarshaller" class="org.springframework.oxm.jaxb.Jaxb2Marshaller">

<property name="classesToBeBound">

<list>

<value>com.sample.vo.ApiUserVo</value>

<value>com.sample.vo.ApiUserListVo</value>

</list>

       </property>

</bean>

 


상기의 방식을 적용하는 경우에는 XML 변환이 필요한 객체가 생성될 때마다 설정을 변경해야한다.


이와 같은 불편함을 해결하기 위해 package 단위로 관련 객체를 검색하는 방법을 구현한 소스를 인터넷에 찾아 약간의 변경을 하였다.


■Package 기준 객체 Scan 소스


public class PackageScanningJaxb2Marshaller  extends Jaxb2Marshaller{


    private static final Logger LOGGER = LoggerFactory.getLogger(ClasspathScanningJaxb2Marshaller.class);

    

    /** base package location **/

    private List<String> basePackages;

    

    /** XML Filter for scanning package **/

    private final TypeFilter[] jaxb2TypeFilters = new TypeFilter[]{

                new AnnotationTypeFilter(XmlRootElement.class, false),

                new AnnotationTypeFilter(XmlType.class, false),

                new AnnotationTypeFilter(XmlSeeAlso.class, false),

                new AnnotationTypeFilter(XmlEnum.class, false)

    };


    

     

     @Required

    public void setBasePackages(List<String> basePackages) { 

    this.basePackages = basePackages; 

    }

    

    /**

     * This method should scan base package with XML related annotation filters to find classes 

     */

    private Class<?>[] getXmlRootElementClasses() throws Exception {

        ClassPathScanningCandidateComponentProvider scanner = new ClassPathScanningCandidateComponentProvider(false);


        for (TypeFilter typeFilter : jaxb2TypeFilters) {

            scanner.addIncludeFilter(typeFilter);       

        }

        

        List<Class<?>> classes = new ArrayList<Class<?>>();

        

        /** Find classes with base package **/

        for (String basePackage : basePackages) {

            Set<BeanDefinition> definitions = scanner.findCandidateComponents(basePackage);

            for (BeanDefinition definition : definitions) {

                String className = definition.getBeanClassName();

               

                LOGGER.info("Found class: {}", className);

                

                classes.add(Class.forName(className));

            }

        }

        

        return classes.toArray(new Class[0]);

    }

    

    @PostConstruct

    public void init() throws Exception {

        setClassesToBeBound(getXmlRootElementClasses());

    }



PacakgeScanningJaxb2Marshaller는 Jaxb2Marshaller를 상속받아 구현하였으며, Bean이 생성된 후 정의된 Package에서 @XML annotation이 정의되어 있는 Class를 찾아 ClassesToBeBound에 해당 Class들을 Set하여 준다.


■ Bean 설정

 <bean id="jaxbMarshaller" class="com.spring.xml.ClasspathScanningJaxb2Marshaller">

    <property name="basePackages">

    <list>

    <value>com.sample</value>

    </list>

</property>

</bean>


■ xmlView 예제


<bean id="xmlView" class="org.springframework.web.servlet.view.xml.MarshallingView">

<!-- constructor-arg>

<bean class="org.springframework.oxm.xstream.XStreamMarshaller" />

</constructor-arg-->

<property name="marshaller" ref="jaxbMarshaller" />

</bean> 


xmlView는 marshller를 jaxbMarshller Bean을 참조한다.



■ Value Object 예제

import java.io.Serializable;


import javax.xml.bind.annotation.XmlAccessType;

import javax.xml.bind.annotation.XmlAccessorType;

import javax.xml.bind.annotation.XmlRootElement;

import javax.xml.bind.annotation.XmlType;

@XmlAccessorType( XmlAccessType.FIELD )

@XmlType( name = "" , 

propOrder = { "clientId", 

    "authCode", 

    "authTime", 

} )

@XmlRootElement(name="ApiUser")

public class ApiUserVo  implements Serializable{

/**

*/

private static final long serialVersionUID = -6929037986045167263L;


private String clientId;

private String authCode;

private String authTime;

public String getClientId() {

return clientId;

}

public void setClientId(String clientId) {

this.clientId = clientId;

}

public String getAuthCode() {

return authCode;

}

public void setAuthCode(String authCode) {

this.authCode = authCode;

}

public String getAuthTime() {

return authTime;

}

public void setAuthTime(String authTime) {

this.authTime = authTime;

}


import java.io.Serializable;

import java.util.ArrayList;

import java.util.List;


import javax.xml.bind.annotation.XmlAccessType;

import javax.xml.bind.annotation.XmlAccessorType;

import javax.xml.bind.annotation.XmlElement;

import javax.xml.bind.annotation.XmlRootElement;


@XmlRootElement(name="ApiUsers")

@XmlAccessorType (XmlAccessType.FIELD)

public class ApiUserListVo implements Serializable{


/**

*/

private static final long serialVersionUID = 1L;

@XmlElement(name="apiUser")

private List<ApiUserVo> apiUsers;

public ApiUserListVo() {

apiUsers = new ArrayList<ApiUserVo>();

}


public List<ApiUserVo> getApiUsers() {

return apiUsers;

}


public void setApiUsers(List<ApiUserVo> apiUsers) {

this.apiUsers = apiUsers;

}



■ Controller 예제

@RequestMapping(value="/sample/searchApiUserXml.do", method={RequestMethod.POST, RequestMethod.GET},  produces={MediaType.APPLICATION_XML_VALUE})


public ModelAndView selectApiUserXml(NexacroMapDTO dto, Model model) throws Exception {

ModelAndView modelAndView = NexacroUtil.getModelAndView(dto);

SBSessionVo sessionVo = SessionUtil.getSession(BaseConstants.DEFAULT_SESSION_NAME);

ApiUserVo vo = new ApiUserVo();

vo.setClientId(sessionVo.getUserId());

List<ApiUserVo> resultList = apiUserService.selectApiUserXml(vo);


ApiUserListVo voList = new ApiUserListVo();

voList.setApiUsers(resultList);

modelAndView.setViewName("xmlView");

modelAndView.addObject("result", voList);

return modelAndView;

}


■ 결과


<ApiUsers>
<apiUser>
<clientId>aaaa</clientId>
<authCode>db78f6a0-344f-485e-a8a9-71a5d54ee7a1</authCode>
<authTime>20184528130242</authTime>
</apiUser>
<apiUser>
<clientId>bbbb</clientId>
<authCode>38bf5d71-c422-42b4-a2f8-f5414fe6d6f0</authCode>
<authTime>20184628130217</authTime>
</apiUser>
</ApiUsers> 


XML을 생성할 때 base package만 정의하면 간단하게 객체를 XML으로 변환이 가능하다.



'Spring Framrwork' 카테고리의 다른 글

Mybatis include sql  (0) 2018.03.23
Spring Framework File Upload with Drag and Drop  (0) 2018.03.16
spring framework interceptor  (0) 2018.03.02
Google Map javascript API 사용법  (0) 2018.02.07
C3 Bar Chart  (0) 2018.02.05
AND

Maven으로 프로젝트를 진행하는 경우 가장 많이 사용하는 Maven Repository에서는 Oracle JDBC driver를 받을 수 없다.


그래서 일반적으로 Dependency 관리를 위해 Nexus를 구성하여 Repository에 Oracle JDBC Driver를 추가하거나 WEB-INF/lib 폴터에 해당 Jar를 넣어서 구성한다.


Oracle JDBC Driver를 제공하는 Repository가 있어 공유하고자 한다.


우선 pom.xml에 Repoistory를 추가한다.


■ pom.xml

<repositories>

        <repository>

            <id>oracle</id>

            <name>ORACLE JDBC Repository</name>

            <url>http://maven.jahia.org/maven2</url>

        </repository>

    </repositories> 


그리고 dependency를 추가한다.


■ dependency 추가

<dependency> 

     <groupId>com.oracle</groupId> 

     <artifactId>ojdbc7</artifactId> 

      <version>12.1.0.2</version> 

 </dependency>

 

http://maven.jahia.org/maven2 웹 사이트에 접속하면 제공하는 Oracle 버전을 찾을 수 있다.


AND

Spring framework을 적용하는 프로젝트에서 사용자 로그인 검증, URI에 대한 권한 체크를 수행하기 위해 Interceptor를 적용하고 있다.


Interceptor에 대해 인터넷에 많은 예제들이 나와 있지만, 실제 프로젝트에 적용하기 위해서는 프로젝트 환경에 맞게 Customizing이 필요하다.


일반적으로 사용자가 로그인하면 사용자에게 권한이 있는 메뉴를 화면에 표시하여 메뉴를 선택하면 해당 이벤트를 처리하는 방식으로 구현한다.


만약 사용자가 시스템에 대한 권한이 없는 URL를 알고 브라우저의 URL 입력 창에 해당 URL을 입력하면 서비스가 처리는 되는 경우도 발생한다.


이러한 상황에서 요청한 URI에 대해 사용자가 권한을 가지고 있는지 검증하기 위해 Interceptor에 적용하는 방법이다.

  


먼저 URI를 검증하고자 하는 Service와 ServiceImpl을 구현한다.


■ 서비스

 public interface AuthCheckService {

public int selectAuthCheck(Object[] params) throws Exception;


}



■ 서비스 구현

public class AuthCheckServiceImpl extends BaseService implements AuthCheckService {


private JdbcTemplate jdbcTemplate;

private String defaultSql;

private String resultColName;

public void setJdbcTemplate(JdbcTemplate jdbcTemplate) {

this.jdbcTemplate = jdbcTemplate;

}


public void setDefaultSql(String defaultSql) {

this.defaultSql = defaultSql;

}


public void setResultColName(String resultColName) {

this.resultColName = resultColName;

}


@Override

public int selectAuthCheck(Object[] params) throws Exception {

Map<String, Object> resultMap = jdbcTemplate.queryForMap(defaultSql, params);

return resultMap.get(resultColName) != null ? Integer.parseInt(String.valueOf(resultMap.get(resultColName))) : 0;

}

 


ServiceImp에서는 JdbcTemplate의 queryForMap 메소드를 호출하여 Map으로 결과를 받는다.


ServiceImp에서는 JdbcTemplate, defaultSql, resultColumnName을 bean설정에서 Property로 세팅된다.


ServiceImpl의 Bean  설정은 예제이다.


■ ServiceImp Bean 설정

<bean id="sbJndiResource" class="org.springframework.jndi.JndiObjectFactoryBean">

<property name="jndiName" value="${sb.jndi.datasource.name}" />

<property name="resourceRef" value="true" />

</bean>  


<bean name="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">

<property name="dataSource" ref="sbJndiResource" />

</bean>

    

 <bean id="authCheckService" class="stis.framework.spring.service.impl.AuthCheckServiceImpl">

<property name="jdbcTemplate" ref="jdbcTemplate" />

<property name="defaultSql" value="#{system['auth.check.sql']}" />

<property name="resultColName" value="cnt" />

</bean>



JdbcTemplate을 사용하기 위해서는 Datasource 설정이 필요하다. 여기서는 JNDI lookup을 통해 WAS에 설정된 Datasource를 찾는다.


defaultSql은 Properties file에 설정되었다. 조회조건에 <, > 가 포함되어 있으면 XML 설정 파일에 정의할 수 없기 때문에 properties 파일에 SQL을 설정한 후 해당 Properties를 참조하도록 하였다.


resultColName은 ServiceImpl에서 Map으로 받은 데이터를 가져오기 위해 필요하다.

즉 SQL의 결과와 동일한 이름을 사용하면 된다.


이제 Interceptor를 개발하기 위한 준비가 되었다.

■ Interceptor 예제

public class SessionInterceptor extends HandlerInterceptorAdapter implements InitializingBean{

private static final Logger LOGGER = LoggerFactory.getLogger(SessionInterceptor.class);

/** Login Check Skip URIs **/

private List<String> skipUris;

/** LOGIN URL **/

private String loginURI;

private SessionVo sessionVo;

private String blockServiceName;

private String authCheckServiceName;

public void setSkipUris(List<String> skipUris) {

this.skipUris = skipUris;

}


@Required

public void setLoginURI(String loginURI) {

this.loginURI = loginURI;

}

@Required

public void setSessionVo(SessionVo sessionVo) {

this.sessionVo = sessionVo;

}



public void setBlockServiceName(String blockServiceName) {

this.blockServiceName = blockServiceName;

}



public void setAuthCheckServiceName(String authCheckServiceName) {

this.authCheckServiceName = authCheckServiceName;

}



/**

* Return boolean

* @param HttpServletRequest

* @param HttpServletResponse

* @paeam Object handler

* @return boolean

* @see 

*/


@Override

public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {

String requestURI = request.getRequestURI();


long startTime = System.currentTimeMillis();

request.setAttribute("startTime", Long.valueOf(startTime));

request.setAttribute("requestURI", requestURI);

request.setAttribute("clientIP", NetUtil.getClinetIP());

/** Session Vo Check if null create new session Vo **/

boolean isSkipUri = false;

if (!NullUtil.isNull(skipUris)) {

isSkipUri = StringUtil.isMatchPattern(skipUris, requestURI);

}

/** Login Check **/

if (!isSkipUri) {

/** Get session value from HTTP Session **/

sessionVo = SessionUtil.getSession(BaseConstants.DEFAULT_SESSION_NAME);

/** Move to Login Page **/

if (NullUtil.isNull(sessionVo)) {

response.sendRedirect(loginURI);

LOGGER.error("You shoud logon the system to use the system(session is null) :: {}", requestURI);

return super.preHandle(request, response, handler);

}

else {

if (NullUtil.isNull(sessionVo.getUserId())) {

response.sendRedirect(loginURI);

LOGGER.error("You shoud logon the system to use the system(userId is null) :: {}", requestURI);

return super.preHandle(request, response, handler);

}

}

}

if (!isSkipUri) {

/** AuthCheck Service **/

if (!NullUtil.isNull(authCheckServiceName)) {

AuthCheckService authCheckService = BeanUtil.getBean(authCheckServiceName);

String[] params = {sessionVo.getUserId(), requestURI};

int cnt = authCheckService.selectAuthCheck(params);

if (cnt <= 0) {

String errMsg = String.format("You don't have authorization to use this URI [%s]", requestURI);

LOGGER.error(errMsg);

throw new AuthException(errMsg);

}

}

}

/** Check Block Service **/

if (!NullUtil.isNull(blockServiceName)) {

BlockService blockService = BeanUtil.getBean(blockServiceName);

List<String> blockLists = blockService.getBlockList();

if (StringUtil.isMatchPattern(blockLists, requestURI)) {

String errMsg = String.format("The service of [%s] is blocked, please contact system administrator", requestURI);

LOGGER.error(errMsg);

throw new AuthException(errMsg);

}

}

return super.preHandle(request, response, handler);

}


/**

* Return void

* @param HttpServletRequest

* @param HttpServletResponse

* @paeam Object handler

* @return void

* @see

*/

@Override

public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView mav) throws Exception {


}


/**

*/

@Override

public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {


String requestURI = request.getAttribute("requestURI").toString();


if (LOGGER.isDebugEnabled()) {

LOGGER.debug("Session Interceptor is called : {}", requestURI);

}

/** If request URI is login or logout, set host name, host address  to session information 

*  When logout, session information is invalidated

* */



long startTime = (Long) request.getAttribute("startTime");

long endTime = System.currentTimeMillis();

LOGGER.info("Request URL::{} Request ClinetIP::{} Response Code::{} Start Time::{} End Time::{}  Elapse Time::{}(ms)",

requestURI, request.getAttribute("clientIP"), response.getStatus(), startTime, endTime, (endTime - startTime));

}



@Override

public void afterPropertiesSet() throws Exception {

}

 


Interceptor에서 Bean이름으로 bean을 찾아서 Bean의 method를 호출한 후 Count를 받아 count가 0보다 작은 경우 Exception을 처리한다.



■Interceptor 설정 예제

<mvc:interceptors>

<mvc:interceptor>

<mvc:mapping path="/**"/>

<bean id="sessionInteceptor" class="stis.framework.spring.interceptor.SessionInterceptor">

<property name="skipUris">

<list>

<value>/sample/login.do</value>

<value>/sample/logout.do</value>

<value>/common/error.do</value>

<value>/rest/*</value>

</list>

</property>

<property name="sessionVo" ref="sessionVo" />

<property name="loginURI" value="/sample/loginPage.do" />

<property name="authCheckServiceName" value="authCheckService" />

</bean>

</mvc:interceptor>

</mvc:interceptors> 


Interceptor 설정 파일에서 authCheckServiceName 값을 ServiceImp 설정의 bean id 를 값으로 전달하였다.


사용자의 URI에 대한 권한 체크하는 SQL은 프로젝트 환경에 따라 다를 수 있기 때문에 여기에서는 SQL은 예외로 한다.



 

'Spring Framrwork' 카테고리의 다른 글

Spring Framework File Upload with Drag and Drop  (0) 2018.03.16
Spring framework XML Marshaller( Jaxb2Marshaller )  (0) 2018.03.09
Google Map javascript API 사용법  (0) 2018.02.07
C3 Bar Chart  (0) 2018.02.05
Spring Batch 모니터링 SQL  (0) 2018.02.03
AND

프로젝트를 진행하면서 서버에서 수행되는 Transaction을 모니터링하기 위해서 APM(Application Performance Tool)을 적용한다.


한국에서 가장 많이 사용하는 APM 툴은 Jenifer로 Trial License로 설치하여 성능을 모니터링을 할 수 있으나 Open Source Scouter를 설치하여 모니터링하는 것도 좋은 방법이라고 생각한다.


Scouter는 git hub(https://github.com/scouter-project/scouter)에 접속하면 다운로드를 받을 수 있고 설치 메뉴얼을 제공한다.


설치 메뉴얼에 따라 쉽게 설치가 가능하다.


Scouter 설치 방법


■ Scouter Download

https://github.com/scouter-project/scouter에서 다운로드 받는다.

최신 버전은 Java 1.8 이상을 지원하기 때문에 서버의 환경의 Jdk 버전을 지원하는 Scouter를 다운로드 받는다.

Jdk1.7을 사용하는 환경에서는 v1.7.3.1을 설치한다.


■ Scouter 압축해제

scouter-all-1.7.3.1.tar.gz 파일을 다운 받아 압축을 해제하면 다음과 같이 Folder가 생성된다(명령어 : tar -zxf scouter-all-1.7.3.1.tar.gz)

 - agent.batch : 배치 모니터링

 - agent.host : Resource(CPU, Memory) 모니터링

 - agent.java : WAS 모니터링

 - server : Collector


■ 설치전 준비사항

user 계정에 SCOUTER_AGENT_DIR을 다음과 같이 설정한다.


 export SCOUTER_AGENT_DIR=/scouter/agent.java


WAS(예:Tomcat)의 setenv.sh에 Agent를 등록한다(위치 : $(TOMCAT_HOME)/bin/setenv.sh


JAVA_OPTS=" ${JAVA_OPTS} -javaagent:${SCOUTER_AGENT_DIR}/scouter.agent.jar"

JAVA_OPTS=" ${JAVA_OPTS} -Dscouter.config=${SCOUTER_AGENT_DIR}/conf/scouter.conf"

JAVA_OPTS=" ${JAVA_OPTS} -Dobj_name=TOMCAT-WAS1"



■ Scouter Collector 설정

Scouter가 설치된 디렉토리의 server/conf 디렉토리로 이동한다.


vi Editor로  scouter.conf 파일을 오픈하여 다음 내용을 입력한다.


# Agent Control and Service Port(Default : TCP 6100)

net_tcp_listen_port=6100


# UDP Receive Port(Default : 6100)

net_udp_listen_port=6100


# DB directory(Default : ./database)

db_dir=./database


# Log directory(Default : ./logs)

log_dir=./logs



여기서 중요한 부분은 Collector가 listen하는 Port를 설정한다. Default로 6100을 사용하고 있으며, Collector가 설치되는 시스템에서 이미 6100 포트를 사용하면 다른 포트로 지정하여야 한다.


설정이 완료되면 server 디렉토리에서 startup.sh Shell을 실행한다.

./startup.sh


[tomcat server]$ ./startup.sh

nohup: redirecting stderr to stdout

  ____                  _

 / ___|  ___ ___  _   _| |_ ___ _ __

 \___ \ / __/   \| | | | __/ _ \ '__|

  ___) | (_| (+) | |_| | ||  __/ |

 |____/ \___\___/ \__,_|\__\___|_|

 Open Source S/W Performance Monitoring

 Scouter version 1.7.3.1

 


■ agent.host 설정

agent.host는 서버의 자원을 모니터링하는 Component 이다.


../agent.host/conf 파일로 이동하여 scouter.conf 파일을 오픈한다.


net_collector_ip=127.0.0.1

net_collector_udp_port=6100

net_collector_tcp_port=6100

cpu_warning_pct=80

cpu_fatal_pct=85

cpu_check_period_ms=60000

cpu_fatal_history=3

cpu_alert_interval_ms=300000

disk_warning_pct=88

disk_fatal_pct=92


net_collector_ip는 Collector가 설치된 서버의 IP 주소를 설정한다.

net_collector_udp_port는 Collector가 Listen하고 있는 UDP Port를 설정한다.

net_collector_tcp_port는 Collector가 Listen하고 있는 TCP Port를 설정한다.


설정이 완료되면 ../agent.host/host.sh Shell을 수행한다.


■ agent.java 설정

agent.java는 WAS의 서비스를 모니터링하기 위한 Component이다.


../agent.java/conf 디렉토리에서 scouter.conf 파일을 오픈한다.

obj_name=TOMCAT-WAS1

net_collector_ip=127.0.0.1

net_collector_udp_port=6100

net_collector_tcp_port=6100

#hook_method_patterns=sample.mybiz.*Biz.*,sample.service.*Service.*

trace_http_client_ip_header_key=X-Forwarded-For

profile_spring_controller_method_parameter_enabled=false

#hook_exception_class_patterns=my.exception.TypedException

profile_fullstack_hooked_exception_enabled=true

#hook_exception_handler_method_patterns=my.AbstractAPIController.fallbackHandler,my.ApiExceptionLoggingFilter.handleNotFoundErrorResponse

#hook_exception_hanlder_exclude_class_patterns=exception.BizException


obj_name는 WAS Instance이름이다. 

net_collector_ip는 Collector가 설치된 서버의 IP 주소를 설정한다.

net_collector_udp_port는 Collector가 Listen하고 있는 UDP Port를 설정한다.

net_collector_tcp_port는 Collector가 Listen하고 있는 TCP Port를 설정한다.


설정이 완료되면 WAS를 재기동한다.

WAS의 로그에 Scouter 관련된 로그가 생성된다.

  ____                  _

 / ___|  ___ ___  _   _| |_ ___ _ __

 \___ \ / __/   \| | | | __/ _ \ '__|

  ___) | (_| (+) | |_| | ||  __/ |

 |____/ \___\___/ \__,_|\__\___|_|

 Open Source S/W Performance Monitoring

 Scouter version 1.7.3.1


20180223 03:49:27 [SCOUTER] Version 1.7.3.1 2017-08-21 11:58 GMT

20180223 03:49:27 [SCOUTER] loaded by system classloader

20180223 03:49:27 [SCOUTER] jar:file:/engn001/scouter/agent.java/scouter.agent.jar

20180223 03:49:27 [SCOUTER] objType:tomcat

 

 이것으로 WAS와 Scouter의 연동이 완료되었다.


■ Scouter Client설치

Scouter Client는 Eclipse Plugin으로 개발되어 있기 때문에 https://github.com/scouter-project/scouter에서 클라이언트 환경에 맞는 scouter client를 다운로드 받는다.


다운로드가 완료되면 압축을 해제한 후 scouter.exe 파일을 실행하면 모니터링이 가능하다.






AND

ARTICLE CATEGORY

분류 전체보기 (56)
Spring Framrwork (33)
Linux (1)
APM (1)
Java (8)
python (0)
ant (1)
chart (1)
OS (1)
tomcat (1)
apache (1)
database (0)

RECENT ARTICLE

RECENT COMMENT

CALENDAR

«   2024/10   »
1 2 3 4 5
6 7 8 9 10 11 12
13 14 15 16 17 18 19
20 21 22 23 24 25 26
27 28 29 30 31

ARCHIVE

LINK