위 현상은 IE에서는 나타나지 않는다. 크롬브라우저에서만 나타난다.

 

 문제가 생기는 이유 : 

 

응답헤더에 ContentType 이외에 파일정보를 Header에 추가하는 코드 때문에 나타는 증상이다.  Internet Explore에서는 다운로드에 대한 파일정보를 헤더에 넣어줘도 이상이 없었지만, 크롬에서는 그것을 취약점 공격을 위한 수단으로 판단한 것으로 보인다. 

예제 코드 : 

HttpServletResponse response = (HttpServletResponse) ActionContext.getContext().get(StrutsStatics.HTTP_RESPONSE); response.setContentType("application/octet-stream; charset=utf-8"); try { //다운로드되는 파일의 정보를 헤더에 추가하는 코드 response.setHeader("Content-Disposition", "attachment; filename=" + URLEncoder.encode(item.getName(), "utf-8") + ";"); } catch (UnsupportedEncodingException ignored) { // do nothing }


 

 해결방법 : application/x-download 를 활용

 

해결 코드 :

HttpServletResponse response = (HttpServletResponse) ActionContext.getContext() .get(StrutsStatics.HTTP_RESPONSE); response.setContentType("application/x-download"); try { HttpServletRequest request = (HttpServletRequest) ActionContext.getContext() .get(StrutsStatics.HTTP_REQUEST); LOG.debug("User-Agent : " + request.getHeader("User-Agent")); if(request.getHeader("User-Agent").contains("Firefox")) { response.setHeader("Content-Disposition", "attachment;filename=\"" + new String(item.getName().getBytes("UTF-8"), "ISO-8859-1") + "\";"); } else { response.setHeader("Content-Disposition", "attachment;filename=\"" + URLEncoder.encode(item.getName(), "utf-8") + "\";"); } } catch (UnsupportedEncodingException ignored) { // do nothing } response.setHeader("Content-Transfer-Encoding", "binary"); LOG.debug("Content-Disposition : " + response.getHeader("Content-Disposition")); LOG.debug("Content Type : " + response.getContentType()); File file = new File(item.getPath()); FileInputStream fileIn = null; ServletOutputStream outstream = null; try { fileIn = new FileInputStream(file); outstream = response.getOutputStream(); byte[] outputByte = new byte[8192]; while (fileIn.read(outputByte, 0, 8192) != -1) { outstream.write(outputByte, 0, 8192); } outstream.flush(); } catch (FileNotFoundException e) { LOG.error(e); } catch (IOException e) { LOG.error(e); } finally { try { fileIn.close(); } catch (IOException e) { } try { outstream.close(); } catch (IOException e) { } }



인터넷을 뒤져봤지만, 쉼표(,)를 다른 문자로 대체하면 된다는 해결책 외에는 딱히 방법이 없었다.

구글을 돌아디나다가 검색해서 찾은 해결책을 기록한다.

출처 : http://stackoverflow.com/questions/2405568/java-webapp-adding-a-content-disposition-header-to-force-browsers-save-as-beh

익스플로러8, 크롬(18.0.1025.151), 파이어폭스(11)에서 정상 동작합니다.

IP주소 문자열을 가지는 목록 List<String> ipList 내부에 있는 IP주소를  정렬하는 방법

사용하는 클래스 java.util.Collections(class), java.util.Comparator(Interface)

정렬하는데 사용식 : 

Collections.sort(ipList, new IpListSortByIp());


IpListSortByIp.java

public class IpListSortByIp implements Comparator<String> {

        @Override
        public int compare(String o1, String o2) {
            try {
                if (InetAddress.getByName(o1).hashCode() > InetAddress.getByName(o2).hashCode()) {
                    return 1;
                } else if (InetAddress.getByName(o1).hashCode() < InetAddress.getByName(o2).hashCode()) {
                    return -1;
                }
            }
            catch (UnknownHostException e) {
                //Exception 처리
            }
            return 0;
        }
    }


간단히 정의를 한다면,

문자열 IP주소를 java.net.InetAddress 객체로 변형하여 그 객체가 가지고 있는 HashCode를 비교하여 정렬하는 방식이다. 

<openjpa-2.1.1-r422266:1148538 nonfatal user error> org.apache.openjpa.persistence.ArgumentException: Attempt to cast instance "...EntityItem@bbef5e8" to PersistenceCapable failed.  Ensure that it has been enhanced.

FailedObject: ...EntityItem@bbef5e8

at org.apache.openjpa.kernel.BrokerImpl.assertPersistenceCapable(BrokerImpl.java:4631)

at org.apache.openjpa.kernel.BrokerImpl.persistInternal(BrokerImpl.java:2610)

at org.apache.openjpa.kernel.BrokerImpl.persist(BrokerImpl.java:2555)

at org.apache.openjpa.kernel.BrokerImpl.persist(BrokerImpl.java:2538)

at org.apache.openjpa.kernel.BrokerImpl.persist(BrokerImpl.java:2442)

at org.apache.openjpa.kernel.DelegatingBroker.persist(DelegatingBroker.java:1077)

at org.apache.openjpa.persistence.EntityManagerImpl.persist(EntityManagerImpl.java:715)

...

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.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:44)

at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:15)

at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:41)

at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:20)

at org.junit.runners.BlockJUnit4ClassRunner.runNotIgnored(BlockJUnit4ClassRunner.java:79)

at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:71)

at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:49)

at org.junit.runners.ParentRunner$3.run(ParentRunner.java:193)

at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:52)

at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:191)

at org.junit.runners.ParentRunner.access$000(ParentRunner.java:42)

at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:184)

at org.junit.internal.runners.statements.RunBefores.evaluate(RunBefores.java:28)

at org.junit.internal.runners.statements.RunAfters.evaluate(RunAfters.java:31)

at org.junit.runners.ParentRunner.run(ParentRunner.java:236)

at org.eclipse.jdt.internal.junit4.runner.JUnit4TestReference.run(JUnit4TestReference.java:50)

at org.eclipse.jdt.internal.junit.runner.TestExecution.run(TestExecution.java:38)

at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:467)

at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:683)

at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:390)

at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:197)



[해결책]

persistence.xml 에 Item들을 추가하세요.

API 참고 URL : http://download.oracle.com/javase/6/docs/api/java/lang/Class.html

   Class는 JVM(Java Virtual Machine)에서 정의된 클래스로 자동 생성해준다. 자바에서 사용하는 모든 클래스에 존재하는 한다. ㅡ_-)>

처음에는 내가 원하는 기능을 만들기 위해, 내가 할 수 있는 것들을 총동원해서(총동원해도 다른 이들보다 역량이 부족하니 턱없이 완성도가 낮은) 만들려고 했었다. 하지만, 요즘 들어 꺠닫게 되는 것이지만 '내가 필요로 하는 것들은 이미 누군가에 의해 만들어져 있다' 라는 경험을 자주 접하고 있다. 인터넷 검색만 제대로 해도 내가 원하는 기능을 '내가 할 수 있는 방법'보다 훨씬 깔끔하고 간결하게 표현한 것들을 많이 찾아볼 수가 있다.


최근 진행하고 있는 프로젝트에서도 '오픈 소스'로 제공하고 있는 다양한 기능들을 활용하여 만족스런 성과를 거두고 있다(아는 만큼 보인다. '오픈소스'도 마찬찬가지다. 아는 만큼 보인다. 제대로 소스와 언어를 이해하는 사람에게는 잘 보이는 것 같다. 나는 아직 안보여!!).

  '오픈소스' 라고 무턱대고 가져다 쓰다보면 나중에 고생할 수가 있다. 우리 수석님이 강조하고 강조하시는 부분이다.



어쨌든!

'객체에 있는 필드명을 이용해서 정보를 가져올 수 있지 않을까?'

라는 기대를 하면서 인터넷 검색을 하다보니

'왠걸?'

이미 JDK에 기본적으로 있는 java.lang.Class 에서 필요한 부분들을 제공하고 있었다. 우선은 간단하게 어떻게 동작하는지 알아볼겸 테스트 케이스를 작성해봤다. 어떻게 정보를 가져올지 찔러본달까?


package javastudy.clazz;

import static org.hamcrest.CoreMatchers.*;
import static org.junit.Assert.*;

import java.lang.reflect.Field;

import org.junit.Before;
import org.junit.Test;

public class ClassTest {

    private TestMakeClass makeClass;
    private String FIELD_NAME = "fieldName";
    private String FIELD_ATTRIBUTE = "filedAttribute";
    private String MAKE_VALUE = "testMakeValue";
    private String MAKE_ATTRIBUTE = "string";

    @Before
    public void setUp() {
        makeClass = new TestMakeClass();
        makeClass.setFieldName(MAKE_VALUE);
        makeClass.setFieldAttribute(MAKE_ATTRIBUTE);
    }

    /**
     * private 접근제어 선언이 되어 있는 경우,
     * field.setAccessible(true) 선언을 해주면 접근이 가능하다. 
     */
    @Test
    public void testClassGetPrivateField() {
        
        Field field = null;
        try {
            field = makeClass.getClass().getDeclaredField(FIELD_NAME);
            field.setAccessible(true);
        } catch (SecurityException e) {
            e.printStackTrace();
        } catch (NoSuchFieldException e) {
            e.printStackTrace();
        }

        String fieldNameValue = null;
        try {
            fieldNameValue = (String) field.get(makeClass);
        } catch (IllegalArgumentException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        }
        assertThat(fieldNameValue, is(makeClass.getFieldName()));
    }
    
    /**
     * private 선언된 필드 정보를 가져오려고 하면 IllegalAccessExceptino이 발생함.
     * 
     * @throws SecurityException
     * @throws NoSuchFieldException
     * @throws IllegalArgumentException
     * @throws IllegalAccessException
     */
    @Test(expected=IllegalAccessException.class)
    public void testClassGetPrivateFieldThrowsEception() throws SecurityException,
    NoSuchFieldException, IllegalArgumentException,
    IllegalAccessException {
        
        Field field = makeClass.getClass().getDeclaredField(FIELD_NAME);
        
        String fieldNameValue = (String) field.get(makeClass);
        assertThat(fieldNameValue, is(makeClass.getFieldName()));
    }
    
    @Test
    public void testClassGetPublicField() throws SecurityException,
    NoSuchFieldException, IllegalArgumentException,
    IllegalAccessException {
        
        Field field = makeClass.getClass().getDeclaredField(FIELD_ATTRIBUTE);
        
        String fieldAttribute = (String) field.get(makeClass);
        assertThat(fieldAttribute, is(makeClass.getFieldAttribute()));
    }
    
    @Test
    public void testClassGetFileds() throws IllegalArgumentException, IllegalAccessException {
        Field[] fields = makeClass.getClass().getFields();
        System.out.println("fields size is[" +fields.length+ "]");
        
        for(Field field : fields) {
            //public 선언되어 있는 필드 정보만 나타난다. 
            String fieldValue = (String) field.get(makeClass);
            System.out.println(fieldValue);
            
        }
    }

    public class TestMakeClass {
        private String fieldName;
        public String fieldAttribute;

        public String getFieldName() {
            return fieldName;
        }

        public void setFieldName(String fieldName) {
            this.fieldName = fieldName;
        }

        public String getFieldAttribute() {
            return fieldAttribute;
        }

        public void setFieldAttribute(String fieldAttribute) {
            this.fieldAttribute = fieldAttribute;
        }

    }
}

이렇게 해서 fieldName을 이용해서 객체의 필드정보를 뽑아내고, 그 필드정보에서 원하는 정보를 가져오는 방법을 익혀봤다.

객체의 필드를 다루기 위해서 숙지해둬야할 것은,  접근하려고 하는 필드의 '접근제어'에 대한 선언이 어떻게 되어있는지 알고 있어야 한다는 것이다.

처음에는


  '뭐야? 이렇게 필드값을 빼낼 수 있으면 캡슐화를 할 이유가 없잖아?'


하고 생각했지만, 테스트 케이스를 작성하면서


  '아, 그 객체 안에 있는 필드명을 모르면 예외만 보겠구나.'


라고 생각하면서 테스트 케이스를 작성해보다가


  '아, 이걸 XML 등에서 정의해놓고 이 xml에서 정보를 가져와서 요렇게 해서 저렇게 할 수 있곘는데?'


라는 '내가 찾던 해결책'을 찾았다.

  이 부분에 대해서는 나중에 공개하도록 하겠다. ^^; 오늘은 여기

관련 사이트 : http://simple.sourceforge.net/home.php


자바를 위한 XML 직렬화처리 및 설정을 해주는 높은 성능의 프레임워크다.

- Simple framework with powerful capabilities

- Can handle cycles in the object graph

- It requires absolutely no configuration

- Extremely rapid development with XML

- Converts to and from human editable XML

- Contains an XML templating system


위의 특징을 가지고 있는 프레임워크다. 자세한 내용은 사이트에 가서 확인하기 바란다.

아래 튜토리얼을 확인하기 바란다.

tutorial : http://simple.sourceforge.net/download/stream/doc/tutorial/tutorial.php



간단한 예제코드

1. 먼저 Simple framework를 다운로드 받는다.

< Example.java >

  
package javastudy.simplexml;

import org.simpleframework.xml.Attribute;
import org.simpleframework.xml.Element;
import org.simpleframework.xml.Root;

@Root
public class Example {
    
    @Element
    private String text;
    
    @Attribute
    private int index;

    public Example() {
        super();
    }

    public Example(String text, int index) {
        super();
        this.text = text;
        this.index = index;
    }

    public String getText() {
        return text;
    }

    public int getIndex() {
        return index;
    }
    
}

<SimpleXmlTest.java> 테스트 코드

  
package javastudy.simplexml;

import static org.hamcrest.CoreMatchers.*;
import static org.junit.Assert.*;

import java.io.File;

import org.junit.Test;
import org.simpleframework.xml.Serializer;
import org.simpleframework.xml.core.Persister;

/**
 * simple Xml serialization 프레임워크를 이용하여 객체를 XML로 변환처리 테스트
 * @author 허니몬
 * 
 * 고려사항 : 
 *  1. Java 객체를 우선 정의해줘야한다.
 */
public class SimpleXmlTest {

    /**
     * Example 클래스의 구조를 example.xml으로 변환하여 xml파일을 생성한다.
     * 이때, 객체 안에 담겨있는 데이터는 @Attribute @Element 애노테이션에 
     * 의해 xml의 attribute와 element로 정의된다. 
     */
    @Test
    public void simpleObjectToXmlTest() {
        Serializer serializer = new Persister();
        Example example = new Example("Example message", 123);
        File result = new File("example.xml");
        
        try {
            serializer.write(example, result);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    
    @Test
    public void simpleXmlToObjectTest() {
        Serializer serializer = new Persister();
        File source = new File("example.xml");
        
        Example example = null;
        try {
            example = serializer.read(Example.class, source);
        } catch (Exception e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        assertThat(example.getText(), is("Example message"));
        assertThat(example.getIndex(), is(123));
    }
}


<example.xml> 위의 코드에서 생성된 xml

  

   Example message


의 형태를 띄게 된다. 그 이외의 상세한 내용에 대해서는...

차근차근 해보도록 하자. 요즘 머리에 주입되는 정보들을 처리하느라 힘들다.




사용용도 : 객체(Java source)를 뼈대로 해서 xml파일을 생성하려고 할 때 사용할 수 있을 것이며,

이렇게 생성된 XML에 담긴 정보를 객체에 주입할여 인스턴스를 생성할 수 있을 것이다.


+ Recent posts