Ajax를 사용하여 페이지를 전환하면, history에 기록되지 않아 Back 버튼이 동작하지 않는다. 물론 해당 방식이 꼭 문제가 되는것은 아니지만, 모바일 웹 어플리케이션 개발에는 문제가 될 수 있다. 예를들어, 플리킹으로 페이징 후 상세로 전환하고 이전으로 돌아오는 씬을 Ajax로 구현하면 이전 페이징 정보를 history에 저장하고 있지 않기 때문에 처음(1페이지)으로 이동하게 된다. 사실 날코딩으로 해당 이슈를 해결할 수 있겠지만 생산성이 떨어진다는 문제가 있다.


좀더 쉽게 해결할 수 있는 방법이 없을까? 라는 물음으로 구글신한테 물어보니, pushState <- 요넘이 검색되더라. 해당 기능을 활용하여 날코딩하니, 깔금하게 동작은 하나 역시나 생산성 및 가독성이 현저하게 떨어지는 문제가 발생했다.


한번더! 도전하는 마음으로 웹전문가 후배님한테 연락 하니, 역쉬 전문가는 전문가 pjax를 알려주더라. 부랴부랴 레퍼런스를 읽어보니, 오호~ 좋더란말이쥐 ^^


샘플 데모를 작성하여 기록용으로 남긴다.


main.html

<!DOCTYPE html>
<html lang="ko">
 <head>
  <meta charset="utf-8">
     <meta http-equiv="X-UA-Compatible" content="IE=edge">
     <meta name="viewport" content="width=device-width, initial-scale=1, user-scalable=no">
     <meta name="description" content="">
     <meta name="author" content="">
    <meta http-equiv="Expires" CONTENT="-1">
    <meta http-equiv="Cache-Control" CONTENT="no-cache">
    <meta http-equiv="Pragma" CONTENT="no-cache">
   
  <title>pjax-demo</title>
  
  <script src="../js/jquery-1.11.3.min.js"></script>
  <script src="../js/jquery.pjax.js"></script>

  
  <script>
   function moveDetail() {
     $.pjax({url: '../pjax/detail.html', container: '#pjax-container'})
   }


   function more() {
    var html = "";
    for(var i = 0;i<50;i++)
     html += "<div onclick='moveDetail()'>append"+i+"</div>";
    
     $("#list").append(html);
   }
  </script>
  
 </head>
 
 <body>
 <div id="pjax-container">
  <div id="list">
   <div onclick="moveDetail()">
    안녕
   </div>
   <div onclick="moveDetail()">
    안녕
   </div>
   <div onclick="moveDetail()">
    안녕
   </div>
   <div onclick="moveDetail()">
    안녕
   </div>   
  </div>
  <div onclick="more()">
   더보기
  </div>
 </div>  
 </body>
</html>


detail.html

 <head>
  <meta charset="utf-8">
     <meta http-equiv="X-UA-Compatible" content="IE=edge">
     <meta name="viewport" content="width=device-width, initial-scale=1, user-scalable=no">
     <meta name="description" content="">
     <meta name="author" content="">
  
     <meta http-equiv="Expires" CONTENT="-1">
     <meta http-equiv="Cache-Control" CONTENT="no-cache">
     <meta http-equiv="Pragma" CONTENT="no-cache">
   
  <title>pjax-demo</title>
  
  <script src="../js/jquery-1.11.3.min.js"></script>
  <script src="../js/jquery.pjax.js"></script>
  
 </head>
 
 <body>
 <div id="pjax-container">
  <div id="detail">
   상세
  </div>
 </div>
 </body>


Posted by 짱가쟁이


MybatisLogInterceptor.java

import java.sql.Statement;

import java.util.ArrayList;

import java.util.List;

import java.util.Map;

import java.util.Properties;


import org.apache.commons.beanutils.PropertyUtils;

import org.apache.ibatis.executor.statement.StatementHandler;

import org.apache.ibatis.mapping.BoundSql;

import org.apache.ibatis.mapping.ParameterMapping;

import org.apache.ibatis.plugin.Interceptor;

import org.apache.ibatis.plugin.Intercepts;

import org.apache.ibatis.plugin.Invocation;

import org.apache.ibatis.plugin.Plugin;

import org.apache.ibatis.plugin.Signature;

import org.apache.ibatis.session.ResultHandler;

import org.slf4j.Logger;

import org.slf4j.LoggerFactory;


@Intercepts({

@Signature(type=StatementHandler.class, method="update", args={Statement.class}),

@Signature(type=StatementHandler.class, method="query", args={Statement.class, ResultHandler.class})

})

public class MybatisLogInterceptor implements Interceptor {


private static final Logger logger = LoggerFactory.getLogger("mybatis.log");

@Override

public Object intercept(Invocation invocation) throws Throwable {

StatementHandler handler = (StatementHandler)invocation.getTarget();

BoundSql boundSql = handler.getBoundSql();

        

List<Object> columnValues = getColumnValues(handler, boundSql);

String sql = getSql(boundSql.getSql(), getTypes(columnValues));

         

        logger.info("=====================================================================");

        logger.info("sql : \n{}", sql);

        logger.info("=====================================================================");

return invocation.proceed(); // 쿼리 실행

}

@Override

public Object plugin(Object target) {

return Plugin.wrap(target, this);

}

@Override

public void setProperties(Properties properties) {

}

private List<Object> getColumnValues(StatementHandler handler, BoundSql boundSql) {

List<Object> columnValues = new ArrayList<Object>();

Object parameter = handler.getParameterHandler().getParameterObject();

List<ParameterMapping> parameterMappingList = boundSql.getParameterMappings();

for(int i = 0;i<parameterMappingList.size();i++) {

try {

if(PropertyUtils.isReadable(parameter, parameterMappingList.get(i).getProperty())) {

Object column = PropertyUtils.getProperty(parameter, parameterMappingList.get(i).getProperty());

columnValues.add(column);

} else {

if(parameter instanceof Map) {

columnValues.add( ((Map<?, ?>) parameter).get(parameterMappingList.get(i).getProperty()) );

} else {

columnValues.add(parameter);

}

}

} catch (Exception e) {

e.printStackTrace();

}

return columnValues;

}

private Object[] getTypes(List<Object> columnValues) {

Object[] object = new Object[columnValues.size()];

for(int i = 0; i < columnValues.size(); i++) {

    object[i] = columnValues.get(i);

   }

   return object;

}    

private String getSql(String sqlStr, Object[] items) {

int seq = 0;

  StringBuffer buf = new StringBuffer();

  int i;

  while ((i = sqlStr.indexOf("?")) >= 0) {

  if(seq == items.length) break; 

 

  buf.append(sqlStr.substring(0, i));

  if (items[seq] instanceof String) {

  buf.append("'" + items[seq] + "'");

  } else if (items[seq] instanceof Integer) {

  buf.append(items[seq]);

  } else if (items[seq] instanceof Long) {

  buf.append(items[seq]);

  } else if (items[seq] == null) {

  buf.append("null");

  } else {

  buf.append(items[seq]);

  }

  sqlStr = sqlStr.substring(i + "?".length());

  seq++;

  }

  buf.append(sqlStr);

  return buf.toString();

  }

}


spring.xml

<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean" >

        <property name="configLocation" value="classpath:/sqlmap/config/mybatis-config.xml"/>

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

        <property name="plugins">

            <array>

                <bean class="xxx.xxx.interceptor.MybatisLogInterceptor"/>

            </array>

        </property>

</bean>


'framework > ibatis' 카테고리의 다른 글

[ibatis] - 오류  (0) 2010.07.06
[ibatis] - resultMap 초기화 문제  (1) 2010.06.25
Posted by 짱가쟁이

Spring MVC로 작업중 입려된 파라미터 검증이 필요하면 공통 성질을 뽑아 aspect로 작업해도 괜찮을 듯 싶다. 공통 성질로는 파라미터 타입, 사이즈, 널체크 등이 있겠지만.. 우선은 널 체크만 작업해보자.


1. @annotation 생성

- 입력값이 필수인 필드명을 array로 받는 단순한 annotation을 생성하자.


사용예) 

@HPNullCheck(parameters={"name", "id", "password"})

public ModelAndView DEMO01(HttpServletRequest request, HttpServletResponse response) {

    ...............

}


@annotation 클래스)

import java.lang.annotation.ElementType;


import java.lang.annotation.Retention;

import java.lang.annotation.RetentionPolicy;

import java.lang.annotation.Target;


@Retention(RetentionPolicy.RUNTIME)

@Target(ElementType.METHOD)

public @interface HPNullCheck {


String[] parameters();

}


2. @annotation 구현 클래스

- Reflection 기능을 사용하여 @annotation 클래스 기능(널 체크)을 구현한다.


RequestFacade 처리)

- 요청 객체가 RequestFacade 이면 아래와 같이 처리한다.

public void requestFacadeNullCheck(RequestFacade request) throws BizException {

Method[] methods = delcaringClass.getMethods();

for(Method method : methods) {

if(method.getName().equals(methodName)) {

HPNullCheck hpNullCheck = (HPNullCheck) method.getAnnotation(HPNullCheck.class);

if(hpNullCheck != null) {

String[] parameters = hpNullCheck.parameters();        

                        if(parameters == null) return;

     

                        for(int i = 0;i<parameters.length;i++) {

                          if(!parameters[i].equals("")) {

                  String value = request.getParameter(parameters[i]);        

                                    

                                    if(StringUtils.isEmpty(value)) {

                                        throw new BizException("ERO001", "["+parameters[i]+"] 값을 확인하세요.");

                                    }

                                }

    }

}

}

}

}


MultipartHttpServletRequest 처리)

- 요쳥 객체가 DefaultMultipartHttpServletRequest 이면 아래와 같이 처리한다.

public void defaultMultipartHttpServletRequestNullCheck(MultipartHttpServletRequest request) throws BizException {

Method[] methods = delcaringClass.getMethods();

for(Method method : methods) {

HPNullCheck hpNullCheck = (HPNullCheck) method.getAnnotation(HPNullCheck.class);

if(hpNullCheck != null) {

String[] parameters = hpNullCheck.parameters();        

if(parameters == null) return;

for(int i = 0;i<parameters.length;i++) {

if(!parameters[i].equals("")) {

String value = request.getParameter(parameters[i]);        

if(StringUtils.isEmpty(value)) {

throw new BizException("ERO001", "["+parameters[i]+"] 값을 확인하세요.");

}

}

}

}

}

}


Aspect로 Controller 클래스의 메소드를 @Around로 걸고 요청객체의 타입에 따라 위와 같이 Reflect를 활용하여 처리한다.


Posted by 짱가쟁이
뭐... 요넘도 마찮가지로 byte array에서 ip4 header만 추출하는 넘임..

Ip4Header.java
- source ip, destination ip, protocol 을 추출

import org.jnetpcap.packet.format.FormatUtils;


public class Ip4Header {


public final static int TCP = 6;

public final static int UDP = 17;

String version;

int     headerLength;

String srcIP;

String dstIP;

int protocol;


public String getVersion() {

return version;

}

public int getHeaderLength() {

return headerLength;

}

public String getSrcIP() {

return srcIP;

}

public String getDstIP() {

return dstIP;

}

public int getProtocol() {

return protocol;

}


public boolean makeHeader(byte[] buf) {

try {

byte[] bTmp = new byte[1];

System.arraycopy(buf, 14, bTmp, 0, bTmp.length);

if(!"4".equals(StringUtil.ascii2hexstr(bTmp).substring(0, 1))) return false;

this.version = StringUtil.ascii2hexstr(bTmp).substring(0, 1);

this.headerLength = Integer.parseInt(StringUtil.ascii2hexstr(bTmp).substring(1, 2)) * 4;

byte[] protocol = new byte[1];

byte[] dstIP = new byte[4];

byte[] srcIP = new byte[4];

System.arraycopy(buf, 23, protocol, 0, protocol.length);

System.arraycopy(buf, 26, srcIP, 0, srcIP.length);

System.arraycopy(buf, 30, dstIP, 0, dstIP.length);

this.protocol = protocol[0];

this.srcIP = FormatUtils.ip(srcIP);

this.dstIP = FormatUtils.ip(dstIP);

return true;

} catch(Exception e) {

e.printStackTrace();

return false;

}

}

}



캡쳐된 패킷 버퍼를 byte[] 로 전환해서 위 클래스에 할당하면 알아서 추출해준다.
byte[] dst = new byte[buffer.capacity()];
buffer.get(dst); 

 
Posted by 짱가쟁이

pcap lib 로 수집된 패킷을 mac address로 필터링을 하려고 하는데.. 이넘의 거지 같은 pcap lib가 thread에 안전하지 않더란 말이쥐.. 더럽게도 다중 쓰레드에서는 각 클래스에 스캔을 뜨는 이 편한 방법을 못쓰고 직접 바이트를 파싱해야 한다능..

그래서 필요한 넘만 직접 파싱해서 하나 만들었다.

EthernetHeader.java
- 패킷을 byte[]로 받아서 source mac, destination mac, type을 추출한다.

public class EthernetHeader {

public final static String IP = "0800";

byte[] srcMac = new byte[6];

byte[] dstMac = new byte[6];

byte[] type = new byte[2];

public byte[] getDstMac() {

return dstMac;

}


public byte[] getSrcMac() {

return srcMac;

}


public byte[] getType() {

return type;

}


public boolean makeHeader(byte[] buf) {

try {

System.arraycopy(buf, 0, dstMac, 0, dstMac.length);

System.arraycopy(buf, 6, srcMac, 0, srcMac.length);

System.arraycopy(buf, 12, type, 0, type.length);

return true;

} catch(Exception e) {

return false;

}

}

public boolean isIp() {

return IP.equals(StringUtil.ascii2hexstr(type));

}

}


 위 코드의 type이라는 넘은 하위 프로토콜의 타입을 의미하며 내는 IP 프로토콜만 사용했기 때문에 위처럼 고정해서 사용했음.

 

ByteBufferHandler 사용해서 패킷 캡쳐하는 부분
 

@Override

public void nextPacket(PcapHeader header, ByteBuffer buffer, Object arg2) {

try {

PcapDumper dumper = (PcapDumper) arg2;

    byte[] dst = new byte[buffer.capacity()];

    buffer.get(dst);

    EthernetHeader ethernetHeader = new EthernetHeader();

    if(!ethernetHeader.makeHeader(dst)) return;

    if(ethernetHeader.isIp()) { // IP 프로토콜

// packet 내의 시간 값 읽어 온다.

String timestampString = JNetPcapUtil.makeTimestampString("yyyyMMddHHmmssSSS", new Date(header.timestampInMillis()));
................
.......... 

 
뭐.. jNetpcap을 사용하면 편하게 패킷을 캡쳐할 수 있고, 필터링 할 수 있지만.. 다중 쓰레드에 안전하지 않다는 것임.
뭐 추후 버전은 concurrency에 안전하게 나올지 모르지만 현재 버전에서는 어쩔 수 없이 편한 방법을 사용하지 못하고 이렇듯 무식하게 직접 파싱을 해줘야 한다. 쩌ㅃ~





 

'framework > jNetPcap' 카테고리의 다른 글

[jnetpcap] - byte[] 에서 ip4 header 추출하기  (2) 2012.02.13
Posted by 짱가쟁이

multipart/form-data로 파일 업로드 하자.

서버

interface

@POST

@Path("/TEST02")

@Consumes("multipart/form-data")

void TEST02(MultipartBody body) throws BizException;


implement

public void TEST02(MultipartBody body) throws BizException {


File file = null;

try {

file = FileUtil.makeFile(body.getAttachment("testFile").getDataHandler().getInputStream()

"D:/upload", body.getAttachmentObject("fileName", String.class) );

} catch (IOException e) {

e.printStackTrace();

}

}


 
inputStrem을 파일로 떨궈주면 땡.. 



클라이언트

public static void excute_post1(String url) throws ClientProtocolException, IOException {

    HttpClient httpclient = new DefaultHttpClient();

    HttpPost httppost = new HttpPost(url);

    httppost.setHeader("content", "multipart/form-data");

    httppost.addHeader("Accept", "application/json; charset=UTF-8" );

        

    FileBody filebody = new FileBody(new File("D:\\test.txt"));


    MultipartEntity reqEntity = new MultipartEntity();

    reqEntity.addPart("testFile", filebody);

    reqEntity.addPart("fileName", new StringBody("aaaa.txt"));

    httppost.setEntity(reqEntity);

    

    HttpResponse response = httpclient.execute(httppost);

    

    httppost.abort();    

    httpclient.getConnectionManager().shutdown();

}









Posted by 짱가쟁이

서버

@GET

@Path("/TEST01/{name}/{id}")

@Produces({"application/json", "application/xml"})

TEST01Res TEST01(@PathParam("name") String name, @PathParam("id") String id) throws BizException;

 
 @Produces({"application/json", "application/xml"}) 설정으로 응답값을 xml, json 스타일을 적용한다. 뭐 클라이언트는 원하는 스타일로 콜만 하면 됨.


클라이언트

 HttpGet httpget = new HttpGet(url);

 httpget.addHeader("Accept", "application/xml; charset=UTF-8" );

 
xml 요청 시
    - httpget.addHeader("Accept", "application/xml; charset=UTF-8" );
josn 요청 시  
    -   
httpget.addHeader("Accept", "application/json; charset=UTF-8" );
Posted by 짱가쟁이

<bean id="weatherProcCronTrigger"
        class="org.springframework.scheduling.quartz.CronTriggerBean">
        <property name="jobDetail"><ref bean="weatherProcMethodInvokingJobDetail"/></property>         
        <property name="cronExpression">
            <value>0/5 * 17-18 * * ?</value>
        </property>
</bean>

<value>0/5 * 17-18 * * ?</value>
    17시 부터 시작해서 18시 59분 55초 까지 동작 (5초 주기로)

Posted by 짱가쟁이

log4j:WARN No appenders could be found for logger (org.springframework.web.context.ContextLoader).
log4j:WARN Please initialize the log4j system properly.

위와 같은 오류가 발생..

web.xml (before)
<listener>
        <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<listener>
        <listener-class>org.springframework.web.util.Log4jConfigListener</listener-class>
</listener>

web.xml 설정을 보니.. 저런 식으로 선언했더니. 위와 같은 오류가 나면서 로그를 제대로 기록하지 못하더라.

혹시나.. 로그 리스너를 먼저 선언해야 할까? 하는 무식한 생각에 위치를 바꿨더니. 제대로 동작하더라.. 뭐밍??

web.xml(after)
<listener>
        <listener-class>org.springframework.web.util.Log4jConfigListener</listener-class>
</listener>
<listener>
        <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>

뭐.. 생각해보니. 스프링컨텍스트로더를 마지막에 올리는게 맞을거 같다는 생각을 해보면서두.. 쩌ㅃ~
Posted by 짱가쟁이
iterator는 value 에서 list 객체를 사용하고 객체의 id를 'field'라 정함.
id를 토대로 해당 리스트의 값에 접근한다.

if 이넘은 test, id 라는 넘을 사용한다는데.. 여기서 사용한 test는 boolean을 체크할때 사용한다.

<s:iterator value="initVo.fields" id="field">
    <s:if test="#field == 'authKey'">
        <s:textfield name='%{initVo.actionName}.%{field}' value='입력하고 싶은 값'> <li><s:property/>    </li></s:textfield><br>
    </s:if>
    <s:else>
        <s:textfield name='%{initVo.actionName}.%{field}' value=''> <li><s:property/></li></s:textfield><br>
    </s:else>
</s:iterator>

#field == 'authKey' <- 리스트의 값이 'authKey'랑 같으면 해당 textfield를 출력하는 코드.
물론 or(||) and(&&) 조건도 사용할 수 있다.
Posted by 짱가쟁이
이전버튼 1 2 3 이전버튼