Java 에서 XML 파일 Validation 수행시 기반이 되는 XSD 파일이 여러개이고 XSD 파일관의 관계가 import, include 등으로 복잡할 경우 SchemaFactory.newSchema(Source schema) 호출 Error 가 발생하는 경우가 있다.


다음의 예제로 XSD 파일 Resolve 방법에 대해 알아보자.


다음과 같이 3개의 XSD 파일이 있고 "main.xsd" 파일이 Root 가 되는 XSD 파일로 a.xsd 와 b.xsd 파일을 import 혹은 include 하고 있다.

  • a.xsd
  • b.xsd
  • main.xsd

위와 같은 구조일 경우 일반적으로 다음과 같이 Schema 를 구현한다.

// read the xsd files
FileInputStream mainStream = new FileInputStream("main.xsd");
FileInputStream aStream = new FileInputStream("a.xsd");
FileInputStream bStream = new FileInputStream("b.xsd");
Source[] sources= new Source[] { new StreamSource(mainStream), new StreamSource(aStream), new StreamSource(bStream) }

// create schema
SchemaFactory factory = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI);
Schema schema = factory.newSchema(sources);

// read xml file for validation
DocumentBuilderFactory documentFactory = DocumentBuilderFactory.newInstance();
documentFactory.setNamespaceAware(true);
DocumentBuilder builder = documentFactory.newDocumentBuilder();
Document doc = builder.parse(new StringInputStream(xml)); // parse the XML into a document object

// validation
Validator validator = schema.newValidator();
validator.validate(new DOMSource(doc));
위의 코드로 수행할 경우 main.xsd 내에서 xsd 파일의 참조순서가 a.xsd, b.xsd 순서가 아니라면 9라인의 factory.newSchema(sources) 에서 에러가 발생하게 된다.


위와 같이 xsd 가 여러파일과의 관계를 맺으며 복잡하게 참조관계를 갖을 경우 org.w3c.dom.ls.LSResourceResolver 인터페이스를 구현한 클래스를 SchemaFactory.setResourceResolver 함수로 등록하면 복잡한 참조관계라도 필요한 파일들을 알아서 로딩 할 수 있게 된다.

// create schema
SchemaFactory factory = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI);
// set resource resolver
factory.setResourceResolver(new ResourceResolver());
Schema schema = factory.newSchema(new StreamSource(new File("main.xsd"));

// read xml file for validation
DocumentBuilderFactory documentFactory = DocumentBuilderFactory.newInstance();
documentFactory.setNamespaceAware(true);
DocumentBuilder builder = documentFactory.newDocumentBuilder();
Document doc = builder.parse(new StringInputStream(xml)); // parse the XML into a document object

// validation
Validator validator = schema.newValidator();
validator.validate(new DOMSource(doc));

class ResourceResolver implements LSResourceResolver {
    private Setresoures = new HashSet();
    @Override
    public LSInput resolveResource(String type, String namespaceURI, String publicId, String systemId, String baseURI) {
         if ( resoures.contains( systemId ) ) {
                return null;
        }
        resoures.add(systemId);
        File file = new File(systemId);
        if ( file.exists() ) {
            LSInput input = new Input();
            try {
                input.setByteStream( new FileInputStream( file ) );
            } catch (FileNotFoundException e) {
                e.printStackTrace();
            }
            return input;
        }
        return null;
    }
}

위의 코드와 같이 LSResourceResolver 인터페이스를 구현한 ResourceResolver 에서 main.xsd 의 참조와 관련한 a.xsd, b.xsd 파일에 대한 정보요청정보를 resolveResource() 함수에서 설정이 가능하다.


 resolveResource() 함수내에서 필요한 a.xsd, b.xsd 파일의 내용이나 위치등을 설정하여 리턴하여주면 main.xsd 내의 참조관계에 따라 필요한 파일정보를 자동으로 요청하여 얻어가게 된다.


Reference: http://stackoverflow.com/questions/3499476/in-java-validate-xml-against-multiple-schemas-located-difference-server-loc

'Java' 카테고리의 다른 글

[Java] Java 플랫폼 종류 요약  (0) 2014.10.15
[JSP] Scriptlet  (0) 2014.08.26
UTF-8 인코딩시 BOM(Byte Order Mark) 문제  (2) 2013.10.30
[Java] Invalid Thread Access Error with Java SWT  (0) 2013.10.02
[Java] Web Resource Compressor Framework  (0) 2013.05.12
Posted by leechwin
,

 앞선 포스팅에서는 SWT Browser 에서 Javascript 수행결과를 얻는 방법으로는 다음의 첫번째 방법을 포스팅 하였는데, 이번 포스팅에서는 두번째 방법에 대해 자세히 알아 보도록 한다.

  • Browser 의 public Object evaluate (String script) 함수를 이용한 리턴값을 이용하는 방법
  • Javascript 핸들러로 org.eclipse.swt.browser.BrowserFunction 을 확장구현한 클래스를 Browser 에 등록하는 방법

 두번째 방법은 SWT Browser(org.eclipse.swt.browser.Browser)에서 Javascript Function Callback 을 등록하고, Browser 에서 등록된 Javascript Function 이 호출될경우 리턴값을 Java 에서 계속 핸들링 할 수 있는 방법이다.


 main.js 파일에 Java 에서 Callback 으로 사용될 함수 javaFunction 함수를 선언해둔다. 이함수는 Javascript 에서 Java 를 호출하는 함수로 Argument 와 함께 호출 할 수 있다.


 Main.Java 파일에서는 SWT Browser 의 callback 으로 사용될 클래스를 구현해야하는데 org.eclipse.swt.browser.BrowserFunction 클래스를 상속받아야한다.

 BrowserFunction 클래스를 상속받아 구현할때 function 함수에서는 arguments 로 Javascript 에서 전달한 Argument 가 전달되어 오고, 비지니스로직 수행후 Javascript 로 다시 return 값을 전달 할 수 있다.

 BrowserFunction 확장 클래스를 구현후 다음과 같이 SWT Browser 와 Javascript Callbck 을 등록해줘야한다.

new CustomFunction (browser, "javaFunction");


// main.js
function sendToJava() {
    var result;
    try {
        result = javaFunction("javascript argument");
    } catch (e) {
        return;
    }
    for (var i = 0; i < result.length; i++) {
         alert( result[i] );
    }
}


// Main.java

final Browser browser = new Browser(parent, SWT.NONE);
browser.setText( "...skip..." ); // skip html content

final BrowserFunction function = new CustomFunction (browser, "javaFunction");

static class CustomFunction extends BrowserFunction {
    CustomFunction (Browser browser, String name) {
        super (browser, name);
    }
    public Object function (Object[] arguments) {
        Object returnValue = new Object[] {
                "Java Argument",
        };
        return returnValue;
    }
}


이와 같이 수행하면 SWTBrowser 에서 Push to invoke Java 버튼을 누를경우 Javascript 의 SendToJava() 함수가 호출되고 내부에서

Java 의 javaFunction 함수가 "javascript argument" 인자와 함께 호출되어 "Java Argument" 라는 리턴값을 Javascript 로 다시넘겨주게된다.




참고


Posted by leechwin
,

SWT Browser(org.eclipse.swt.browser.Browser)에서 Runtime 에 해당 Browser 상에서 Javascript 의 수행결과를 얻고 싶을 경우가 있다.

 SWT Browser 에서 Javascript 수행결과를 얻는 방법으로는 다음과 같이 2가지 방법이 있다.

  • Browser 의 public Object evaluate (String script) 함수를 이용한 리턴값을 이용하는 방법
  • Javascript 핸들러로 org.eclipse.swt.browser.BrowserFunction 을 확장구현한 클래스를 Browser 에 등록하는 방법

 여기에서는 Browser 의 evaluate 함수를 이용하여 Javascript 수행 값을 얻는 방법을 알아 보도록 한다.

 evaluate 함수는 Javascript 의 eval 함수와 비슷하다고 생각하면 되는데, 이용 방법은 간단하다.

 다음과 같이 Browser 의 evaluate 함수에 Runtime 에 수행할 Javascript Code 를 넣어주면 된다.

final Browser browser = new Browser(parent, SWT.NONE);
browser.setJavascriptEnabled(true);    	
browser.addProgressListener(new ProgressAdapter() {
    public void completed(ProgressEvent event) {
        String bodyHTML = (String) browser.evaluate("return document.body;");
        // TODO:
    }
});

evaluate 함수는 다음과 같이 5개의 type 을 리턴한다.

  • javascript null or undefined -> null
  • javascript number -> java.lang.Double
  • javascript string -> java.lang.String
  • javascript boolean -> java.lang.Boolean
  • javascript array whose elements are all of supported types -> java.lang.Object[]


evaluate 함수를 사용할때 주의 할 점은 Browser 객체와 같은 Thread 에서 수행하면 evaluate 함수가 Async 하게 동작하므로 제대로 된 값을 리턴받지 못하는 경우가 발생 할 수 있다. 따라서 다른 Thread 혹은 ProgressListener 등에서 수행되도록 작성하여야 한다.

Posted by leechwin
,