'개발/자바'에 해당되는 글 41건

  1. 2009.02.04 oracle db 에서 데이타를 읽어서 xml을 생성하는 예제
  2. 2009.02.03 JAXP
  3. 2009.01.20 이클립스에서 Error retrieving "feature.xml". [error in opening zip file] 에러날때
  4. 2009.01.16 [펌]log4j 설정법
  5. 2009.01.16 Java (j2se 1.4.1) 메모리 영역 구조
  6. 2009.01.15 [JVMSTAT]jvmstat 3.0 설치방법
  7. 2009.01.08 printStackTrace를 Log4j에 출력하기
  8. 2008.12.30 Java FX for Eclipse
  9. 2008.12.29 log4j.properties 사용 방법 (ConversionPattern 사용 방법)
  10. 2008.12.12 질문 sun.io.MalformedInputException에 대해서
2009. 2. 4. 09:54

oracle db 에서 데이타를 읽어서 xml을 생성하는 예제




명령행 매개변수로 전달된 상품 정보를 가지고 XML 문서(product.xml)를 생성하는 Java Application 과 만들어
진 XML 문서를 DB 테이블에 CLOB 형으로 저장하는 Java Application 을 작성하세요. XML 문서를 만드는 과정
은 비어 있는 Document 객체에 노드들을 추가하는 방식을 활용하십시오.

[ 명령행 매개변수로 입력하는 상품 정보 ]
상품ID, 상품명, 가격, 

   다음과 같은 구조로 XML 파일이 생성되도록 구현한다.
<PRODUCTLIST>      
    <PRODUCT> 
          <ID>...........</ID> 
          <NAME>...........</NAME> 
          <PRICE>...........</PRICE> 
    </PRODUCT> 
   </PRODUCTLIST>


- 저장되는 DB 테이블 스펙
    테이블명 : xmltable
    필드 스펙 : productid   char(5),   xmlcontent clob

  - DB 필드의 CLOB 형 필드에 product.xml 을 저장하는 기능은 PreparedStatement 의 setCharacterStream() 
이라는 메서드를 사용한다.




com.noaats.xml000.CreatXML.java

===================================================

package com.noaats.xml000;

import java.io.StringReader;
import java.io.StringWriter;
import java.sql.Connection;
import java.sql.Driver;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.util.Properties;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.transform.OutputKeys;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;

import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Text;


public class CreateXML {

 private Document xml;

 private String jdbcDriver = "oracle.jdbc.driver.OracleDriver";

 private String jdbcUrl = "jdbc:oracle:thin:@localhost:1521:XE";

 private String userName = "scott";

 private String userPwd = "tiger";
 
 private String insertSql = "insert into xmltable values ( ?,?) ";

 /**
  * @param args
  *            상품정보를 입력 받는다. args[0] id args[1] name args[2] price
  */
 public static void main(String[] args) {
  CreateXML cxml = new CreateXML(args);
 }

 public CreateXML(String[] args) {
  process(args);
 }

 protected void process(String[] args) {
  if (args.length == 0) {
   System.out.println("Usage : CreateXML ");
  } else {
   Element product = createXMLDocument();
   createID(product, args[0]);
   createName(product, args[1]);
   createPrice(product, args[2]);
   saveXML(args[0], product);
  }
 }

 private void saveXML(String id, Element product) {
  Connection conn = null;
  PreparedStatement ps = null;
  try {
   Driver driver = (Driver)Class.forName(jdbcDriver).newInstance();
   conn = DriverManager.getConnection(jdbcUrl, userName, userPwd);
   
  } catch (Exception sqle) {
   sqle.printStackTrace();
  }

  TransformerFactory factory = TransformerFactory.newInstance();
  try {
   StringWriter sw = new StringWriter();
   Properties output = new Properties();
   output.setProperty(OutputKeys.INDENT, "yes");
   Transformer transformer = factory.newTransformer();
   transformer.setOutputProperties(output);
   transformer.transform(new DOMSource(xml), new StreamResult(sw));
   System.out.println(sw.getBuffer().toString());
   ps = conn.prepareStatement(insertSql);
   ps.setString(1,id);
   ps.setCharacterStream(2,new StringReader(sw.getBuffer().toString()),sw.getBuffer().toString().length());
   ps.execute();
   ps.close();
   conn.close();
  } catch (Exception e) {
   e.printStackTrace();
  } finally {
   try {
    if (ps != null) ps.close();
    if (conn != null ) conn.close();
   } catch (Exception ee) {
    ee.printStackTrace();
   }
  }
    
 }

 private void createPrice(Element product, String price) {
  Element priceElement = createElementWithValue("PRICE", price);
  product.appendChild(priceElement);

 }

 private void createName(Element product, String name) {
  Element productElement = createElementWithValue("PRODUCT", name);
  product.appendChild(productElement);
 }

 private void createID(Element product, String id) {
  Element idElement = createElementWithValue("ID", id);
  // Text idTest = xml.createTextNode(id);
  // idElement.appendChild(idTest);
  product.appendChild(idElement);
 }

 private Element createElementWithValue(String elementName,
   String elementValue) {
  Element element = xml.createElement(elementName);
  Text textNode = xml.createTextNode(elementValue);
  element.appendChild(textNode);
  return element;
 }

 protected Element createXMLDocument() {
  Element product = null;
  DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
  try {
   DocumentBuilder builder = factory.newDocumentBuilder();
   xml = builder.newDocument();
   Element productlist = xml.createElement("PRODUCTLIST");
   xml.appendChild(productlist);

   product = xml.createElement("PRODUCT");
   productlist.appendChild(product);

  } catch (Exception e) {
   e.printStackTrace();
  }
  return product;
 }

}

출처 : http://blog.bagesoft.com/794

2009. 2. 3. 19:26

JAXP




XML의 소개

Extensible Markup Language (XML)은 웹 브라우져를 통해 한 페이지의 내용 요소들을 구분하는 방법입니다. XML의 문법은 HTML과 비슷합니다. 사실, XML은 HTML이 쓰이는 여러 곳에서 사용될 수 있습니다. 아래에 예가 있습니다. JDC Tech Tip의 목차가 HTML이 아니라 XML로 저장되어 있다고 가정합시다. 아래와 같은 HTML 코드 대신

<html>
<body>
<h1>JDC Tech Tip Index</h1>
<ol><li>
<a
href="http://developer.java.sun.com/developer/TechTips/2000/tt0509.html#tip1">
Random Access for Files
</a>
</li></ol>
</body>
</html>

아래와 같이 나타납니다.

<?xml version="1.0" encoding="UTF-8"?>
<tips>
<author id="glen" fullName="Glen McCluskey"/>
<tip title="Random Access for Files"
     author="glen"
     htmlURL="http://developer.java.sun.com/developer/TechTips/2000/tt0509.html#tip1"
     textURL="http://developer.java.sun.com/developer/TechTips/txtarchive/May00_GlenM.txt">
</tip>
</tips>

XML과 HTML의 코드 사이의 유사성을 보십시오. 두가지 모두 문서가 계층적 엘리먼트로 구성되어 있으며, 얼레먼트는 꺾음 괄호로 구분되어 있습니다.HTML 엘리먼트가 거의 그렇듯이, XML 엘리먼트는 시작 태그와 그 뒤에 나타나는 데이터, 그리고 끝 태그로 구성되어 있습니다.

<element>element data</element>

역시 HTML과 마찬가지로, XML 엘리먼트는 속성을 지정할 수 있습니다. 위의 XML의 예에서, 각 <tip> 엘리먼트는 몇개의 속성을 갖고 있습니다. 'title' 속성은 팁의 이름을 나타내며, 'author' 속성은 저자의 이름을 나타냅니다. 그리고 'htmlURL'과 'textURL' 속성은 두가지의 다른 형식으로 저장된 팁의 링크를 나타냅니다.

두개의 마크업 언어 사이의 유사점은 HTML에서 XML로 옮겨가는데 아주 중요한 장점입니다. 어렵게 습득한 HTML 기술들을 그대로 이용할 수 있기 때문입니다. 그러나, "왜 XML로 바꾸라고 해서 고생하게 만들지?"라는 의문이 들것입니다. 이 의문에 대한 대답으로, 위의 XML 예를 다시 한번 보십시오. 그리고 문법적인 측면이 아니라 의미적인 측면에서 살펴보십시오. HTML은 문서를 어떻게 형식화하는지를 말해주지만, XML은 문서의 내용에 대해 말해줍니다. 이 능력은 매우 강력한 것입니다. XML에서, 클라이언트는 그 데이터를 자신에게 가장 적합한 형식으로 재구성할 수 있습니다. 서버가 제공하는 출력 양식에 구애받지 않습니다. 중요한것은, XML 형식은 가독성을 희생시키지 않으면서도 파서의 편의를 위해 설계되었다는 것입니다. XML은 문서의 구조에 대해 반드시 지켜야 할 약속을 가지고 있습니다. 몇가지를 보자면, 시작 태그는 언제나 끝 태그를 가지고 있어야 하며, 엘리먼트는 정확히 내포관계를 가져야 하고, 모든 속성은 값을 가져야 합니다. 이런 엄격함은 XML 문서의 파싱과 변환을 HTML의 변환에 비해서 높은 신뢰성을 갖게 합니다.

XML과 HTML의 유사성은 그 시작이 갖기 때문에 생깁니다. HTML은 강력한 마크업 언어인 SGML을 간략화한 것입니다. SGML은 개발자가 자신만의 어휘, 즉 문법/태그 등을 만들어낼 수도 있는, 거의 무엇이든지 할 수 있는 "주방의 싱크대"같은 마크업 언어입니다. HTML은 미리 정의된 어휘를 가진, SGML에 대한 아주 작은 부분입니다. 따라서 HTML은 대략 1992년 경에나 쓸만했던, 표현을 위한 요소들입니다. SGML과 HTML은 모두 문제가 있습니다. SGML은 모든것을 합니다. 그래서 아주 복잡합니다. HTML은 간단합니다. 그러나 파싱 규칙이 엉성하고, 어휘는 확장을 할 수 없습니다. 이에 비해 XML은 SGML을 합리적으로 간략화했습니다. 이것의 목적은 복잡하지 않으면서도 SGML의 중요한 목적을 모두 지원합니다. SGML이 "주방의 싱크대"라면 XML은 "스위스 아미 나이프-맥가이버칼... -_-" 입니다.

이런 장점 때문에, XML은 몇가지 응용 분야에서는 HTML을 대체하는것 이상의 일을 할 수 있습니다. 이것은 SGML을 대체할 수도 있으며, SGML의 복잡성 때문에 적용이 불가능했던 분야에 대한 새로운 방법이 될 수 있습니다. XML을 어디에 쓸 것인지에 관계없이, 프로그래밍 언어는 자바를 이용할 것입니다. 자바 언어는 Simple API for XML (SAX)와 Document Object Model (DOM) 인터페이스를 이용하여 XML 문서를 파싱하기 위한 고수준의 도구를 제공하기 때문에, XML을 직접 파싱하기 위한 자신만의 코드를 작성할 수 있습니다. SAX와 DOM 파서는 다른 몇가지 언어에서도 구현한 표준입니다. 자바 프로그래밍 언어에서, Java(tm) API for XML Parsing (JAXP)를 이용해서 이런 파서들을 인스턴스화 할 수 있습니다.

이 팁의 코드를 실행하려면, http://java.sun.com/xml/download.html에서 JAXP와 SAX/DOM 파서를 다운로드 받아야 합니다. 그리고 http://www.megginson.com/SAX/Java에서 SAX 2.0도 다운로드 받아야 합니다. 그리고 classpath를 jaxp, 파서, 그리고 sax2의 JAR 파일을 포함하도록 고치는 것을 잊지 마십시오.

 


 

 SAX API의 이용

SAX API는 XML 문서를 다루기 위한 직력 메커니즘을 제공합니다. 이것은 XML-DEV 메일링 리스트의 멤버들에 의해 서로 다른 벤더들이 구현할 수 있는 표준 인터페이스의 집합으로 개발되었습니다. SAX 모델은 파서가 문서를 읽어나가면서 마크업을 만나면 이벤트 핸들러를 호출하는 형식으로 파싱하도록 합니다. SAX의 첫번째 구현은 1998년 5월에 나왔습니다. 그리고 SAX 2.0은 2000년 5월에 나왔습니다. (이 팁에서 사용된 코드들은 SAX2용입니다.)

마크업 발생에 대한 신호를 위해 SAX2를 사용할 때 할 일은, 몇개의 메소드와 인터페이스를 작성하는 것입니다. 이 인터페이스들에서 ContentHandler 인터페이스가 가장 중요합니다. 이것은 XML 문서를 파싱하기 위한 각각의 단계들을 위한 몇개의 메소드를 선언합니다. 많은 경우, 이런 메소드 중에서 몇개만 이용하게 됩니다. 예를 들어, 아래의 코드는 하나의 ContentHandler 메소드 (startElement)만을 이용하며, 이것을 이용하여 XML Tech Tip 목록에서 HTML 페이지를 생성합니다.

    import java.io.*;
    import java.net.*;
    import java.util.*;
    import javax.xml.parsers.*;
    import org.xml.sax.*;
    import org.xml.sax.helpers.*;
    /**
     * 팁의 제목들의 목록을 보여주고 이에 대한 HTML과 text 버젼의 문서에
     * 연결하는 간단한 HTML 페이지를 생성한다.
     */
    public class UseSAX2 extends DefaultHandler
    {
        StringBuffer htmlOut;

        public String toString()
        {
            if (htmlOut != null)
                return htmlOut.toString();
            return super.toString();    
        }
    
        public void startElement(String namespace, String localName, String qName, Attributes atts)
        {
            if (localName.equals("tip")) 
            {
                String title = atts.getValue("title");
                String html = atts.getValue("htmlURL");
                String text = atts.getValue("textURL");
                htmlOut.append("<br>");
                htmlOut.append("<A HREF=");
                htmlOut.append(html);
                htmlOut.append(">HTML</A> <A HREF=");
                htmlOut.append(text);
                htmlOut.append(">TEXT</A> ");
                htmlOut.append(title);
            }
        }

        public void processWithSAX(String urlString) throws Exception
        {
            System.out.println("Processing URL " + urlString);
            htmlOut = new StringBuffer("<HTML><BODY><H1>JDC Tech Tips Archive</H1>");
            SAXParserFactory spf = SAXParserFactory.newInstance();
            SAXParser sp = spf.newSAXParser();
            ParserAdapter pa = new ParserAdapter(sp.getParser());
            pa.setContentHandler(this);
            pa.parse(urlString);
            htmlOut.append("</BODY></HTML>");
        }

        public static void main(String[] args)
        {
            try
            {
                UseSAX2 us = new UseSAX2();
                us.processWithSAX(args[0]);
                String output = us.toString();
                System.out.println("Saving result to " + args[1]);
                FileWriter fw = new FileWriter(args[1]);
                fw.write(output, 0, output.length());
                fw.flush();
            }
            catch (Throwable t)
            {
                t.printStackTrace();
            }
        }
    }

이 프로그램을 테스트하기 위해, 이 팁의 앞부분의 XML의 소개에 있는 XML 문서를 이용하거나, http://staff.develop.com/halloway/TechTips/TechTipArchive.xml에 있는 조금 더 긴 문서를 이용할 수 있습니다. 이렇게 받은 XML 문서를 컴퓨터의 로컬 디렉토리에 TechtipArchive.xml로 저장하십시오. 아래와 같은 명령어로 HTML 문서를 생성할 수 있습니다.

    java UseSAX2 file:TechTipArchive.xml SimpleList.html

그리고 SimpleList.html을 웹브라우져를 통해서 보고, 최근 팁의 텍스트 버젼과 HTML 버젼의 링크를 따라가 보십시오. (실제 시나리오는 위의 코드를 클라이언트의 브라우져나 서버의 서블릿/jsp 안에 합쳐 넣어야 할것입니다.)

위 코드에는 몇가지 흥미로운 부분이 있습니다. 파서를 생성하는 부분을 보십시오.

    SAXParserFactory spf = SAXParserFactory.newInstance();
    SAXParser sp = spf.newSAXParser();

JAXP에서, SAXParser 클래스는 직접 생성하지 않고, factory 메소드인 newSAXParser()를 이용합니다. 이것은 서로 다르게 구현된 코드들이 소스코드의 변경 없이 끼워넣기가 가능하도록 하기 위한 것입니다. factory는 이 외에도 namespace의 지원, 인증 등의 향상된 파싱 요소들을 지원합니다.JAXP 파서의 인스턴스를 가진 뒤에라도, 아직은 파싱할 준비가 되지 않았습니다. 현재의 JAXP 파서는 SAX 1.0만 지원합니다. SAX 2.0을 지원하도록 하기 위해서, 파서를 ParserAdapter로 감싸야 합니다.

    ParserAdapter pa = new ParserAdapter(sp.getParser());

ParserAdapter 클래스는 SAX2 다운로드의 일부분이며, 지금 있는 SAX1 파서에 SAX2의 기능을 추가합니다.

ContentHandler 인터페이스를 구현하는 대신, UseSAX는 DefaultHandler 클래스를 상속받습니다. DefaultHandler는 모든 ContentHandler의 메소드에 대해 아무것도 하지 않는 비어있는 구현을 제공하는 어댑터 클래스입니다. 따라서 오버라이드 하고싶은 메소드만 구현하면 됩니다.

startElement() 메소드가 실제 작업을 수행합니다. 프로그램은 팁을 제목 목록을 보여주면 되기 때문에, <tip> 엘리먼트만 있으면 되며, <tips>, <author> 엘리먼트는 무시합니다. startElement 메소드는 엘리먼트의 이름을 체크하고 현재 엘리먼트가 <tip>일 때에만 계속해서 작업을 수행합니다. 또 이 메소드는 엘리먼트의 속성을 Attributes 레퍼런스를 통해 접근하도록 합니다. 따라서 팁의 이름과 name, htmlURL, textURL을 추출하는 것이 쉽습니다.

이 예의 결과는 최근의 Tech Tips의 목록을 보여주는 HTML 문서입니다. HTML을 직접 만들어도 됩니다. 하지만 이것을 XML로 하고, SAX 코드를 작성하면 추가적인 유연성을 제공할 수 있습니다. 다른 누군가 Tech Tip을 날짜별로, 저자별로 정렬하거나 몇가지 제약사항을 가지고 필터링을 하고자 한다면, 각각에 대한 파싱 코드를 이용하여 하나의 XML 문서에서 해당 문서를 생성할 수 있습니다.

불행히도, XML 데이터가 복잡해짐에 따라, 위의 샘플도 점점 코딩하고 수정하기가 어려워집니다. 위의 예는 두가지 문제를 가지고 있습니다. 첫번째는, HTML 문서를 생성하기 위한 코드가 단지 문자열을 조작하는 것 뿐이고, 따라서 어딘가에 있을 '>', '/'를 놓지게 된다는 것입니다. 두번째로, SAX API는 많은 것을 기억하지 못합니다. 즉, 앞의 어딘가에 있었던 엘리먼트를 참조하고자 한다면, 앞에서 파싱한 엘리먼트를 저장하기 위한 자신만의 state machine을 만들어야 한다는 뜻입니다.

Document Object Model (DOM) API가 이 두가지 문제점을 해결합니다.

 


 

DOM API의 이용

DOM API는 SAX API와는 완전히 다른 문서 처리 모델을 기반으로 하고 있습니다. (SAX와 같이) 한번에 문서의 한 부분을 읽는 대신, DOM 파서는 문서 전체를 읽습니다. 그리고 읽고 수정할 수 있는 프로그램 코드를 만들 수 있도록 문서 전체를 트리 구조로 만듭니다. SAX와 DOM의 차이점을 간단히 말하자면, 순차적/읽기 전용 접근과 임의/읽기/쓰기 접근의 차이입니다.

DOM API의 핵심은 Document와 Node 인터페이스입니다. Document는 XML 문서를 나타내는 최상위 객체입니다. Document는 데이터를 Node의 트리 형태로 저장하고 있으며, Node는 element, attribute, 또는 다른 형태의 내용물을 저장하는 기본 타입입니다. 또, Document는 새로운 Node를 생성하기 위한 factory로도 동작합니다. Node는 트리에서의 하나의 데이터를 표현하며, 트리에 있어서의 필요한 동작을 제공합니다. 노드에 대해 그 부모/형제/자식 노드를 찾을 수 있습니다. 또 Node를 추가/삭제함으로써 문서를 수정할 수 있습니다.

DOM API의 예제를 보기 위해, 위의 SAX에서 보여졌던 XML 문서를 처리해 봅시다. 이번에는 저자별로 묶어서 출력합니다. 이것은 몇가지의 작업을 더 해야 합니다. 아래에 코드가 있습니다.

    //UseDOM.java
    import java.io.*;
    import java.net.*;
    import java.util.*;
    import javax.xml.parsers.*;
    import org.w3c.dom.*;

    public class UseDOM
    {
        private Document outputDoc;
        private Element body;
        private Element html;

        private HashMap authors = new HashMap ();
    
        public String toString ()
        {
            if (html != null)
            {
                return html.toString ();
            }
            return super.toString();
        }

        public void processWithDOM (String urlString)
            throws Exception
        {
            System.out.println ("Processing URL " + urlString);
            DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance ();
            DocumentBuilder db = dbf.newDocumentBuilder ();
            Document doc = db.parse (urlString);
            Element elem = doc.getDocumentElement ();
            NodeList nl = elem.getElementsByTagName ("author");
            for (int n=0; n<nl.getLength (); n++)
            {
                Element author = (Element)nl.item (n);
                String id = author.getAttribute ("id");
                String fullName = author.getAttribute ("fullName");
                Element h2 = outputDoc.createElement ("H2");
                body.appendChild (h2);
                h2.appendChild (outputDoc.createTextNode ("by " + fullName));
                Element list = outputDoc.createElement ("OL");
                body.appendChild (list);
                authors.put (id, list);
            }
            NodeList nlTips = elem.getElementsByTagName ("tip");
            for (int i=0; <nlTips.getLength (); i++)
            {
                Element tip = (Element)nlTips.item (i);
                String title = tip.getAttribute ("title");
                String htmlURL = tip.getAttribute ("htmlURL");
                String author = tip.getAttribute ("author");
                Node list = (Node) authors.get (author);
                Node item = list.appendChild (outputDoc.createElement ("LI"));
                Element a = outputDoc.createElement ("A");
                item.appendChild (a);
                a.appendChild(outputDoc.createTextNode (title));
                a.setAttribute ("HREF", htmlURL);
            }
        }

        public void createHTMLDoc (String heading)
            throws ParserConfigurationException  
        {
            DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance ();
            DocumentBuilder db = dbf.newDocumentBuilder ();
            outputDoc = db.newDocument ();
            html = outputDoc.createElement ("HTML");
            outputDoc.appendChild (html);
            body = outputDoc.createElement ("BODY"); 
            html.appendChild (body);
            Element h1 = outputDoc.createElement ("H1");
            body.appendChild (h1);
            h1.appendChild (outputDoc.createTextNode (heading));
        }

        public static void main (String[] args)
        {
            try
            {
                UseDOM ud = new UseDOM ();
                ud.createHTMLDoc ("JDC Tech Tips Archive");
                ud.processWithDOM (args[0]);
                String htmlOut = ud.toString ();
                System.out.println ("Saving result to " + args[1]);
                FileWriter fw = new FileWriter (args[1]);
                fw.write (htmlOut, 0, htmlOut.length ());
                fw.flush ();
            }
            catch (Throwable t)
            {
                t.printStackTrace ();
            }
        }
    }

XML을 TechTipArchive.xml로 저장했다고 가정합시다. 아래와 같은 명령어로 코드를 실행할 수 있습니다.

    java UseDOM file:TechTipArchive.xml ListByAuthor.html

그리고 웹브라우져로 ListByAuthor.html을 열어서 저자별로 구성된 팁의 목록을 봅니다.

코드가 동작하는 방법을 알기 위해, createHTMLDoc 메소드부터 살펴봅시다. 이 메소드는 outputDoc Document를 생성합니다. 이것은 나중에 HTML 출력을 만드는데 이용됩니다. SAX와 마찬가지로, 파서는 factory 메소드를 통해 생성됩니다. 그러나 여기서는 factory 메소드가 DocumentBuilderFactory 클래스에 있습니다. createHTMLDoc의 아래쪽 반은 HTML의 기본적인 요소들을 생성합니다.

    outputDoc.appendChild (html);
    body = outputDoc.createElement ("BODY"); 
    html.appendChild (body);
    Element h1 = outputDoc.createElement ("H1");
    body.appendChild (h1);
    h1.appendChild (outputDoc.createTextNode (heading));

이 코드를 SAX 예제에 있는 HTML의 요소를 생성하는 코드와 비교해 보십시오.

    //direct string manipulation from SAX example
    htmlOut = new StringBuffer ("<HTML><BODY><H1>JDC Tech Tips Archive</H1>");

DOM API를 이용하여 문서를 생성하는 것은 문자열을 직접 다루는 것만큼 간단하거나 빠르지 않습니다. 그러나 (특히 큰 문서에 대해서) 에러의 발생이 적습니다.

useDOM 예제에서 가장 중요한 부분은 processWithDOM 메소드입니다. 이 메소드는 두가지 일을 합니다. (1) 저자 엘리먼트를 찾고 이것을 출력으로 제공합니다. (2) 팁을 찾고 저자별로 구분하여 출력으로 제공합니다. 이 단계들은 각각 문서의 최상위 엘리먼트에 접근할 수 있어야 합니다. 이것은 getDocumentElement () 메소드를 통해 수행됩니다. 저자 정보는 <author> 엘리먼트에 들어있습니다. 이런 엘리먼트는 최상위 레벨에서 getElementsByTagName ("author") 메소드를 호출하면 찾을 수 있습니다. getElementsByTagName 메소드는 NodeList를 리턴합니다. 이것은 간단한 Node의 컬렉션입니다. 그리고 각각의 Node는 getAttribute 메소드를 호출하기 위해 Element로 형변환됩니다. getAttribute 메소드는 저자의 아이디와 이름을 입력으로 받습니다. 각각의 저자는 2수준의 머리말로 목록이 만들어집니다. 출력 문서는 저자의 이름을 담고 있는 <H2> 요소를 생성해 냅니다. Node를 추가하기 위해서는 2개의 단계가 필요합니다. 첫번째로 출력 문서에서 createElement와 같은 factory 메소드를 통해 Node를 생성합니다. 다음으로 노드가 appendChild 메소드를 통해 추가됩니다. 노드는 자신을 생성한 문서에만 추가될 수 있습니다.

저자 머리말이 제 위치에 놓이게 되면, 각각의 팁에 링크를 생성할 차례입니다. <tip> 요소를 <author> 엘리먼트를 찾을때와 같은 방법으로 getElementsByTagName을 이용해 찾습니다. 팁 속성을 추출하는 방법도 비슷합니다. 한가지 차이점은 노드를 어디에 추가할 것인가를 결정하는 것입니다. 저자가 다르면 다른 목록에 추가되어야 합니다. 이 작업을 위한 기초 작업으로 저자 엘리먼트를 처리할 때에 <OL> 노드를 추가하고 저자의 아이디로 HashMap 인덱싱을 했습니다. 이제, 팁의 저자 아이디 속성을 팁이 추가될 적당한 <OL> 노드를 찾는데 이용할 수 있습니다.

XML에 대해 더 깊이 다룬 내용을 보시려면, 2000년에 Addison-Wesley에서 출판한 Neil Bradley의 The XML Companion을 참고하십시오. JAXP에 대한 더 자세한 정보는http://java.sun.com/xml/index.html의 Java(tm) Technology와 XML 페이지를 참고하십시오. SAX2에 대한 더 자세한 정보는 http://www.megginson.com/SAX/index.html을 참고하십시오. DOM 표준은 http://www.w3.org/TR/REC-DOM-Level-1에 있습니다. 

출처 http://www.javastudy.co.kr/docs/techtips/000627.html

2009. 1. 20. 11:38

이클립스에서 Error retrieving "feature.xml". [error in opening zip file] 에러날때




" I guess you download the package and unzip or just put it into Eclipse's directory, without using the Software Updates tool in Help menu in Eclipse." 라고 하는군요

1. Delete the site.xml file in your Eclipse directory.

와 같이 하면 됩니다 ^^
2009. 1. 16. 16:43

[펌]log4j 설정법




=============================================

본문서는 자유롭게 배포/복사 할수 있지만

이문서의 저자에 대한 언급을 삭제하시면 안됩니다

저자 : GoodBug (unicorn@jakartaproject.com)

최초 : http://www.jakartaproject.com 

=============================================


LOG4J

I. 들어가면서.. 그리고 log4j


log4j는 자바 어플리케이션에서 빠르고 효과적으로 로깅 할 수 있도록 도와주는 오픈 소스 프로젝트입니다.


로깅(logging)은 코드의 가독성을 떨어뜨리는 단점이 있지만 애플리케이션에 문제가 있을 때 개발자가 자세한 상황을 파악할 수 있도록 해 주며 테스팅시 빠질 수 없는 요소입니다.


아마도 여러분들은 여러 어플리케이션이 추가되면서 각 개발자들만의 독특한 로깅방식이 서로 썩이고 얽혀서 화면에 나타나는것을 많이 봤을겁니다 -_-;
즉 로깅방법을 통일할 필요가 있는것이죠. 모든 개발자가 특정 포맷에 맞추어서 로깅 한다면 한결 로깅하기도 편하겠지요


오픈 소스 프로젝트인 Log4j는 개발자들이 매우 손쉽고 다양한 형태로 로깅을 할 수 있도록 도와줍니다. 성능또한 우수해 더이상 System.out.println을 사용할 필요가 없습니다.



II. 다운로드


다운로드 http://logging.apache.org/log4j/docs/download.html

매뉴얼 http://logging.apache.org/log4j/docs/documentation.html

API spec http://logging.apache.org/log4j/docs/api/index.html



III. LOG4J 구조


일단 log4j를 잘 모르지만 그 구조만 살짝 살펴보고 넘어갑시다

log4j는 크게 3가지 요소로 구성되며 그 구조는 다음과 같습니다

① Logger(Category) : 로깅 메세지를 Appender에 전달합니다.

② Appender : 전달된 로깅 메세지를 파일에다 기록할 것인지, 콘솔에 출력할 것인지

                   아니면 DB에 저장할 것인지 매개체 역활을 합니다.

③ Layout : Appender가 어디에 출력할 것인지 결정했다면 어떤 형식으로 출력할 것이지

                출력 layout을 결졍합니다.

쉽죠?



IV. LOG4J 로깅 레벨


log4j는 다양한 로깅레벨을 지원합니다.


① FATAL : 가장 크리티컬한 에러가 일어 났을 때 사용합니다.

② ERROR : 일반 에러가 일어 났을 때 사용합니다.

③ WARN : 에러는 아니지만 주의할 필요가 있을 때 사용합니다.

④ INFO : 일반 정보를 나타낼 때 사용합니다.

⑤ DEBUG : 일반 정보를 상세히 나타낼 때 사용합니다.


만약 로깅 레벨을 WARN 으로 설정하였다면 그 이상 레벨만 로깅하게 됩니다.

즉 WARN, ERROR, FATAL 의 로깅이 됩니다.



V. 샘플코드 1


jsp에서 사용하는 예제가 없어 만들어 봤습니다.


test.jsp


<%@ page contentType="text/html;charset=MS949"
 import="org.apache.log4j.Logger" %>

<%!
 static Logger logger = Logger.getLogger("test.jsp");
%>

<%
logger.fatal("fatal!!");

logger.fatal("fatal2!!", new NullPointerException("널입니다요"));

logger.error("error!", new NumberFormatException());

logger.error("error!2");

logger.warn("warn");

logger.info("info");

logger.debug("debug");
%>


결과 콘솔화면








static Logger logger = Logger.getLogger("test.jsp");

static 메소드 getLogger를 통해 logger 인스턴스를 가져옵니다.
getLogger에는 파라미터로 스트링 혹은 클래스를 사용하는데 jsp에서는 클래스를 파라미터로 주기에는 좀 애매합니다. 그냥 스트링으로 주도록 하지요


logger.fatal("fatal!!");
logger.fatal("fatal2!!", new NullPointerException("널입니다요"));
  
logger에 fatal 레벨의 메세지를 전달합니다. 다음 두가지 메소드를 지원하는군요

fatal(Object message)

fatal(Object message, Throwable t)

각 레벨마다 위처럼 두가지 메소드를 지원합니다.


지원 메쏘드
logger.fatal(Object message) logger.fatal(Object message, Throwable t)
logger.error(Object message) logger.error(Object message, Throwable t)
logger.warn(Object message) logger.warn(Object message, Throwable t)
logger.info(Object message) logger.info(Object message, Throwable t)
logger.debug(Object message) logger.debug(Object message, Throwable t)


VI. 샘플코드 2


서블릿의 경우 다음과 같이 코딩하면 되겠군요

TestServlet.java


import javax.servlet.*;
import javax.servlet.http.*;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

public class TestServlet extends HttpServlet {


    static Logger logger = Logger.getLogger(TestServlet.class);


    public void init(ServletConfig config) throws ServletException {
         super.init(config);
    }


    public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {

         try {
              ...
   
              logger.info("Hellow World~");

              ...

          } catch (Exception e) {
              logger.error("Error at TestServlet", e);
          }
     }
}



VII. LOG4J 설정


log4j 설정은 프로그램 내에서 할 수 있지만 설정파일을 사용함으로서 좀더 유연하게 log4j환경을 만들 수 있습니다.


프로그램에서 설정

<%@ page contentType="text/html;charset=MS949"
 import="org.apache.log4j.*,java.io.* "
%>

<%!
 static Logger logger = Logger.getLogger("log4j.jsp");
%>

<%
String layout = "%d %-5p [%t] %-17c{2} (%13F:%L) %3x - %m%n";
String logfilename = "DailyLog.log";
String datePattern = ".yyyy-MM-dd ";

PatternLayout patternlayout = new PatternLayout(layout);
DailyRollingFileAppender appender = new DailyRollingFileAppender(patternlayout, logfilename, datePattern);
logger.addAppender(appender);
logger.setLevel(Level.INFO);
logger.fatal("fatal!!");
%>


property 파일에 설정
log4j.properties를 만들어 /WEB-INF/classes 밑에 놓으세요



log4j.rootLogger=INFO, stdout, rolling

log4j.appender.stdout=org.apache.log4j.ConsoleAppender

log4j.appender.stdout.layout=org.apache.log4j.PatternLayout

log4j.appender.stdout.layout.ConversionPattern=%d %-5p [%t] %-17c{2} (%13F:%L) %3x - %m%n

log4j.appender.rolling=org.apache.log4j.DailyRollingFileAppender

log4j.appender.rolling.File=output.log

log4j.appender.rolling.Append=true

log4j.appender.rolling.MaxFileSize=500KB

log4j.appender.rolling.DatePattern='.'yyyy-MM-dd

log4j.appender.rolling.layout=org.apache.log4j.PatternLayout

log4j.appender.rolling.layout.ConversionPattern=%d %-5p [%t] %-17c{2} (%13F:%L) %3x - %m%n


#최상위 카테고리에 INFO로 레벨 설정 및 appender로 stdout, rolling을 정의

log4j.rootLogger=INFO, stdout, rolling

#stdout 어펜더는 콘솔에 뿌리겠다는 정의

log4j.appender.stdout=org.apache.log4j.ConsoleAppender

#stdout 어펜더는 patternlayout을 사용하겠다는 정의

log4j.appender.stdout.layout=org.apache.log4j.PatternLayout

#페턴은 다음과 같이 포맷팅 하겠다는 것을 정의

log4j.appender.stdout.layout.ConversionPattern=%d %-5p [%t] %-17c{2} (%13F:%L) %3x - %m%n


#역시나 rolling 어펜더는 파일로 처리한다라고 정의

log4j.appender.rolling=org.apache.log4j.DailyRollingFileAppender

#로그 파일 이름은 output.log

log4j.appender.rolling.File=output.log

#true면 톰캣을 내렸다 올려도 파일이 리셋되지 않습니다.

log4j.appender.rolling.Append=true

#파일 최대 사이즈는 500KB로 설정

log4j.appender.rolling.MaxFileSize=500KB

#파일 포맷은 output.log.2005-03-10 으로 관리하겠다고 정의

log4j.appender.rolling.DatePattern='.'yyyy-MM-dd

#역시나 rolling 어펜더는 패턴 레이아웃을 사용하겠다고 정의

log4j.appender.rolling.layout=org.apache.log4j.PatternLayout

#rolling 어펜더는 패턴 레이아웃 포맷

log4j.appender.rolling.layout.ConversionPattern=%d %-5p [%t] %-17c{2} (%13F:%L) %3x - %m%n



VIII. 설정 포맷


로그파일명 포맷 (DatePattern)
로그파일명 포맷입니다. 날짜, 시간 및 분단위로까지 로그 파일을 분리할 수 있습니다.

형식 설명
'.'yyyy-MM 매달 첫번째날에 로그파일을 변경합니다
'.'yyyy-ww 매주의 시작시 로그파일을 변경합니다.
'.'yyyy-MM-dd 매일 자정에 로그파일을 변경합니다.
'.'yyyy-MM-dd-a 자정과 정오에 로그파일을 변경합니다.
'.'yyyy-MM-dd-HH 매 시간의 시작마다 로그파일을 변경합니다.
'.'yyyy-MM-dd-HH-mm 매분마다 로그파일을 변경합니다.



PatternLayout 포맷
로그자체를 어떤 포맷으로 남길지 결정합니다.
layout에는 HTMLLayout, PatternLayout, SimpleLayout, XMLLayout등이 있으며 PatternLayout이 일반적으로 가장 많이 쓰입니다.


형식 설명
%p debug, info, warn, error, fatal 등의 priority 가 출력된다.
%m 로그내용이 출력됩니다
%d 로깅 이벤트가 발생한 시간을 기록합니다.
포맷은 %d{HH:mm:ss, SSS}, %d{yyyy MMM dd HH:mm:ss, SSS}같은 형태로 사용하며 SimpleDateFormat에 따른 포맷팅을 하면 된다
%t 로그이벤트가 발생된 쓰레드의 이름을 출력합니다.
%% % 표시를 출력하기 위해 사용한다.
%n 플랫폼 종속적인 개행문자가 출력된다. \r\n 또는 \n 일것이다.
%c 카테고리를 표시합니다
예) 카테고리가 a.b.c 처럼 되어있다면 %c{2}는 b.c가 출력됩니다.
%C 클래스명을 포시합니다.
예) 클래스구조가 org.apache.xyz.SomeClass 처럼 되어있다면 %C{2}는 xyz.SomeClass 가 출력됩니다
%F 로깅이 발생한 프로그램 파일명을 나타냅니다.
%l 로깅이 발생한 caller의 정보를 나타냅니다
%L 로깅이 발생한 caller의 라인수를 나타냅니다
%M 로깅이 발생한 method 이름을 나타냅니다.
%r 어플리케이션 시작 이후 부터 로깅이 발생한 시점의 시간(milliseconds)
%x 로깅이 발생한 thread와 관련된 NDC(nested diagnostic context)를 출력합니다.
%X 로깅이 발생한 thread와 관련된 MDC(mapped diagnostic context)를 출력합니다.

예시) (같은 색끼리 보시면 됩니다)

위의 test.jsp를 다음 포맷으로 출력해본다면

[%c] [%C] [%d] [%F] [%l] [%L] [%m] [%M] [%n] [%p] [%r] [%t] [%x] [%X]는 다음과 같다

[test.jsp] [org.apache.jsp.test_jsp] [2005-03-10 12:37:23,561] [test_jsp.java] [org.apache.jsp.test_jsp._jspService(test_jsp.java:64)] [64] [fatal!!] [_jspService] [개행] [FATAL] [765567] [http-8080-Processor25] [] []

2009. 1. 16. 10:08

Java (j2se 1.4.1) 메모리 영역 구조




출처 : http://cafe.naver.com/ArticleRead.nhn?articleid=69&sc=e0d433130b4a2d9c18&query=gc+java+%B8%DE%B8%F0%B8%AE&clubid=10092843

1. Sun Microsystyems의 자바 HotSpot VM은 힙을 세 개의 영역으로 나누고 있다.
 힙의 세 영역은 다음과 같다:
 1) Permanent space: JVM 클래스와 메소드 개체를 위해 쓰인다.
 2) Old object space: 만들어진지 좀 된 개체들을 위해 쓰인다.
 3) New object space: 새로 생성된 개체들을 위해 쓰인다.

New object space는 세 부분으로 다시 나누어진. 
모든 새로 생성된 개체들이 가는 Eden, 그리고 그 개체들이 나이들게(Old) 되기 전에 가는 Survivor space1(From), Survivor space2(To) 가 있다.

 

2. Garbage Collector
프로그램은 프로그램을 진행하면서 데이터들을 저장하는 것이 필요하다. 
데이터들은 모두 메모리에 저장이 되는데, 저장할 데이터가 있으면 메모리의 일정 공간을 할당받아서 사용하게 된다. 
프로그램 내에서 사용하게 되는 메모리를 'heap'이라고 한다. 
더 이상 사용되지 않는 데이터에게 메모리를 계속 할당해 주는 것은 메모리를 낭비하는 것이므로, 그 데이터가 사용하던 메모리를 회수하는 것이 필요하다. 
이러한 사용되지 않는 메모리에 대한 회수를 'Garbage Collection'이라고 한다. 자바에서는 프로그램이 사용하는 메모리를 JVM(Java Virtual Machine)이 모두 관리한다.

 

3. OutOfMemory Error 및 해결방법
자바는 객체, 변수등의 생성과 동시에 메모리(Heap)를 차지하게 되고, 문제는 이 객체와 변수를 너무 많이 발생시킴으로 해서 현재 할당된 메모리(Heap)를 초과하게 된다 
그래서 더이상 할당받을 메모리(Heap)가 부족하게 되면 OutOfMemory Error 발생하게 된다. 
OutOfMemory Error 해결방법으로는 
jdk1.4에서 -XX:+PrintGCDetails -XX:+PrintGCTimeStamps -XX:+PrintHeapAtGC 옵션을 사용한 GC는 현재상태의 Heap메모리 정보출력 한다. 
GC정보를 통하여 New, Old, Perm 등의 영역중 실제 어느 부분이 부족하여 OutOfMemory가 발생하는지 찾은후 부족한 영역의 충분하 size를조절해 주는 방법으로 해결할 수 있다.

 
4. Heap layout 할당에 영향을 주는 스위치들


5. New Generation 메모리 할당 공식
   Eden = NewSize - ((NewSize/(SurvivorRatio + 2)) * 2)
   From space = (NewSize - Eden)/2
   To space = (NewSize - Eden)/2

 
6. Old Generation 메모리 할당 공식
   Old = Xmx - MaxNewSize
 

7.  JVM 스위치 설정 예제
 1) 현재 
http://www.affis.net 서비스는 2200개의 Jsp파일을 가지고 있고 주로 정적인 페이지들이므로 Jsp 파일 로딩에 필요한 Perm size 위주로 메모리 튜닝을 하였다.
 2) 현재 
http://club.affis.net 서비스는 어플리케이션 동적이페이지들로 작성되어 있고 어플리케이션처리에 필요한  New size 위주로 메모리 튜닝을 하였다.
 

8. 맺음말
OutOfMemory 발생한다면 GC로그를 찍어본다. 
로그를 분석해보면 New(eden, from, to), Old, Perm 등의 영역중에서 GC가 발생해도  메모리 영역이 계속 100%로 할당되는 영역이 보일것이다. 
부족한 영역에 충분한 size 메모리를 할당해 주면 OutOfMemory 해결 된다.
그러나 부족한 영역에 계속해서 메모리 할당을 해주어도 사용률이 100%가 나온다면 프로그램 누수일수 있으니 프로그램을 점검해 봐야 할 것이다.

2009. 1. 15. 16:25

[JVMSTAT]jvmstat 3.0 설치방법





# java JVM Memory Perf 분석


- Tools : jvmstat-5.zip
   J2SE 1.5 이상


- 구성 환경 :
 Host A에 설치 동작중인 Java Applicaton( Unix )에 대한 메모리 이용율을
 Host B( Windows )에서 Remote로 확인


- Host A 설치내용
 J2SE 1.5 이상의 버젼이 설치 되었는지 확인
  참고 : Java 1.5부터는 jvmstat의 내용이 일부 포함되어 배포가 됨.

 jvmstat를 위한 Home Directory 생성 ( /usr/local/jvmstat )
 $ mkdir /usr/local/jvmstat/

 RMI Connection 권한 설정 파일 생성
 $ cd /usr/local/jvmstat/
 $ vi jstatd.all.policy
 grant codebase "file:${java.home}/../lib/tools.jar" {
     permission java.security.AllPermission;
 };

 jvmstat를 위한 환경 변경 설정
 $ export JAVA_HOME=/usr/local/java
 $ export JVMSTAT_HOME=/usr/local/jvmstat
 $ export PATH=$JVMSTAT_HOME/bin:$JAVA_HOME/bin:$PATH

 jstatd 실행 ( jvmstat 2.0의 perfagent와 같음. )
 $ cd /usr/local/jvmstat/
 $ cat start.sh
 #!/bin/bash

 export JVMSTAT_HOME=/usr/local/jvmstat
 export JAVA_HOME=/usr/local/jdk1.5.0_02
 export PATH=$JAVA_HOME/bin:$JVMSTAT_HOME:$PATH

 # RMI Registry Start
 $JAVA_HOME/bin/rmiregistry &

 # JSTAT PerfAgent Start
 nohup \
 $JAVA_HOME/bin/jstatd \
 -J-Djava.security.policy=$JVMSTAT_HOME/jstatd.all.policy
 \
 -J-Djava.rmi.server.logCalls=true \
 &
 $ sh start.sh

 local test 수행
 $ jps
 $ jstat -gcutil 11216 1000 10   ( 11216은 jvmps에서 vmid )

 Java RMI Connection을 위한 설정
 $ vi /etc/hosts
 위 hostname ( backup )을 RMI로 호출될 Interface의 IP로 변경 한다.
 127.0.0.1       localhost.localdomain localhost
 192.168.1.117 backup


- Host B 설치 내용
 J2SE 1.5 이상의 버젼이 설치 되었는지 확인

 jvmstat-3.zip를 C:\에 압축 풀기 ( visualgc에 대한 내용만이 포함되어 있음. )

 jvmstat를 위한 환경 변경 설정
 SET JAVA_HOME="C:\Program Files\Java\jdk1.5.0_02"
 SET JVMSTAT_HOME="C:\jvmstat"
 SET JVMSTAT_JAVA_HOME="C:\Progra~1\Java\jdk1.5.0_02"
 SET PATH=%JVMSTAT_HOME%\bat;%JAVA_HOME%\bin;%PATH%

 remote test 수행 ( Host A와의 RMI 통신여부 확인 )
 C:\jvmstat\bat\> jps 192.168.1.117
 C:\jvmstat\bat\> jstat -gcutil rmi://11216@192.168.1.117 1000 10


- Host A의 Java Application 모니터링
 상기의 설치 내용이 정상적으로 완료 확인

 Host B에서
 C:\jvmstat\bat\> visualgc 11216@192.168.1.117:1099 5000
  11216 : vmid
  192.168.1.117 : host A's IP Address
  1099 : Host A's RMI TCP Port
  5000 : Interval , millisecond

2009. 1. 8. 16:06

printStackTrace를 Log4j에 출력하기




package com.qnsolv.wipi.exception;

import java.io.ByteArrayOutputStream;
import java.io.PrintStream;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

public class ExceptionUtil
{
 Log log = null;
 
 
 public ExceptionUtil( Object obj)
 {
  log = LogFactory.getLog(obj.getClass().getName());
 }
 public ExceptionUtil()
 {
  log = LogFactory.getLog(this.getClass().getName());
 }
 public void writePrintStckTrace(Exception e)
 {
  writePrintStckTrace(e, "");
 }
 
 public void writePrintStckTrace(Exception e, String MIN)
 {
  try
  {
   ByteArrayOutputStream out = new ByteArrayOutputStream();
   PrintStream pinrtStream = new PrintStream(new ByteArrayOutputStream());
   e.printStackTrace(pinrtStream);
   String stackTraceString = out.toString();
   log.error("MIN=" + MIN + ":" + stackTraceString.toString());
  }
  catch (Exception e2)
  {
  }
 }
}

2008. 12. 30. 19:22

Java FX for Eclipse




JavaFX가 넷빈즈에서만 돌아가는 줄 알았는데 역시나 이클립스 용 플러그인이 나와 있습니다.


위 링크를 통해 다운로드 받은 파일을 eclipse 설치 폴더에 압축해제하면 설치는 완료 됩니다.

사용방법은 다음과 같습니다.

아래 이미지와 내용은 해당 사이트의 Getting Start 문서를 옮겨왔습니다.

JavaFX SDK 설치

JavaFX SDK for Windows or Mac Os X 다운로드 받아 설치합니다.

Eclipse 설치

Eclipse 3.4 이상이 필요하며. Eclipse IDE for Java EE Developers for Windows or Mac OS X 설치가 필요합니다.

Plugin 설치

  1. plugin을 다운로드 받습니다.:
  2. com.sun.javafx.eclipse.feature-win32-win32-x86-1.0.zip for Windows
  3. com.sun.javafx.eclipse.feature-macosx-carbon-x86-1.0.zip for Mac OS X
  4. eclipse 설치 디렉토리에 압축을 해제합니다.
  5. Eclipse 을 실행합니다.

JavaFX 프로젝트 만들기 와 JavaFX SDK 설정하기
  1. 이클립스를 시작하면 이클립스 workbench의 기본 레이 아웃이 보입니다.
    New empty workbench
  2. Package Explorer에서 오른쪽 클릭하여 New > Java Project 를 선택합니다.
    New Java Project menu
  3. Create a new JavaTM project 로 자바 프로젝트를 생성합니다. 
    New empty workbench
  4. MyJavaFXProject 라는 프로젝트 명과 Finish 버튼을 누릅니다. 새로운 프로젝트가 Package Explorer에 나타납니다. 
    New project created
  5. 프로젝트 이름을 선택하고 오른쪽 클릭하여 JavaFX > Add JavaFX Nature 를 선택합니다.
    Add JavaFX nature to Java Project
  6. 그럼 JavaFX perspective가 활성화 됩니다. JavaFX nature 를 Java Project 에 처음 추가 하는것이라면 JAVAFX_HOME 속성값을 물어볼수도 있습니다.
    Error, JavaFX SDK home not set up.
    JAVAFX_HOME 속성 설정은 아래 단계를 따릅니다.:
    1. 다이얼로그 메시지의 OK 버튼을 누르고 No thanks, I prefer Cancel 버튼을 누릅니다.
    2. 아래와 같은 다이얼로그 창이 그면 JAVAFX_HOME 값을 (empty)로 추가시킵니다.
      Preferences: Classpath Variables
    3. 그리고 Edit... 버튼을 누르면 아래와 같은 창이 뜹니다.
      Edit classpath variable
    4. Folder... 버튼을 눌러 the JavaFX SDK 설치 폴더를 지정해줍니다. 
      Select instalation folder of JavaFX SDK
    5. 폴더선택창의 OK 버튼을 누르고, 변수 Edit 창의 OK 버튼을 누릅니다. 
      Result of setting JAVAFX_HOME property
    6. Preferences 다이얼로그의 OK버튼을 누릅니다. 그럼 JAVAFX_HOME 변수의 세팅이 끝납니다. 만약 Cancel 버튼을 누르면 에러 다이얼로그 창이 뜨며 다시 반복하여 묻게 됩니다.
  7. 이로서 첫번째 JavaFX Project 생성이 완료되었습니다. 
    Opened JavaFX Perspective with Java Project with JavaFX nature

Adding Files

  1. MyJavaFXProject 를 확장해보자. src 폴더를 선택하고. 오른쪽 클릭을 하여 New > Package를 선택한다. 그리고 package 이름에 com.sun.eclipse.example를 입력해보자.
    Adds new Empty Script file into folder.
  2. src/com.sun.eclipse.example 폴더를 오른쪽 클릭후. New > Empty Script 를 선택하자.
    Adds new Empty Script file into folder.
  3. New File 마법사가 보일것이다. Name 필드에 Main 이라고 입력하자.
    New file wizzard
  4. Finish 버튼을 누르면 새로운 JavaFX 스크립트 창이 열린다. 
    New empty FX script
  5. Snippets view를 보자. 여기엔 애플리케이션 개발을 도와줄 여러 가지 code snippets 이 있다. Applications 항목을 열어 Stage를 선택해보자.
    Snippets View
  6. Stage 노드를 editor window로 Drag and drop 하면. Insert Template: Stage 다이얼로그가 보인다. title 변수값을 Hello World 로 바꾸자.
    Result after drop
  7. Insert 버튼을 누르면 template 의 결과가 소스 코드로 변환되어 insert 된다. 
    Result in editor
  8. Mission accomplished, Sir!

Running

Eclipse 로 실행하는 것은 매우 쉽다.

  1. 툴바의 Run 버튼을 누른다. 
    Run icon
  2. 첫번째 실행이라면 Edit Configuration 다이얼로그가 보인다. 
    Configuration
    모든 옵션은 default 값이다. 가장 흥미로운 부분은 Profile - Target 옵션이다. 여기서 실행환경을 조정할 수 있다. 가능한 옵션은 아래와 같다.:
    • Desktop profile - Run as Application
    • Desktop profile - Run as Applet
    • Desktop profile - Run with WebStart
    • Mobile Profile[?] - Run in Emulator
  3.  Run 버튼을 눌러 애플리케이션을 작동시킨다. console 창에 실행 프로그레스가 뜰것이다. 
    Running application

Building

Eclipse 에서의 Build 작업은 default로 자동으로 제공된다. Project 메뉴에서 Build와 관련된 좀 더 많은 옵션을 볼 수 있다.

Configuration Options

  1. Package Explorer 에서 프로젝트 선택 후 오른쪽 클릭하여 Properties를 선택한다. 
    Menu: Project Properties
  2. Properties 창이 보이면 JavaFX Defaults 를 선택한다.
    Properties: JavaFX Defaults
    여기서 JavaFX Project의 실행과 디플로이 프로세스 동안 사용되는 몇가지 기본 파라미터 값을 수정할 수 있다. 좀 더 많은 정보는 JavaFX Packager Documentation 을 통해 볼 수 있다.

JavaFX Help

이 플러그인은 JavaFX SDK documentation로의 접근을 매우 쉽게 해준다. Help > Java FX > JavaFX Script Documentation or press Ctrl + F1 를 누르므로서 Document에 접근 할 수 있다.
JavaFX SDK Help

Footnotes:

  • 언제든 JAVAFX_HOME 변수를 아래와 같은 방법으로 수정 할 수 있다.:
    1. Open Window > Preferences
    2. Expand Java > Build Path and select Classpath Variables
    3. Edit JAVAFX_HOME
  • SDK의 profile을 아래 단계를 통해 수정 할 수 있다.:
    1. Right click on project name. Select Properties > Java Build Path > Libraries
    2. Double click on JavaFX System Library. The Edit Library dialog appears. Here you can set the profile to use.
      Dialog: Edit JavaFX System Library


출처 : http://javaora.tistory.com/entry/Java-FX-for-Eclipse
2008. 12. 29. 15:27

log4j.properties 사용 방법 (ConversionPattern 사용 방법)





log Level 정의

 FATAL : 가장 크리티컬한 에러가 일어 났을 때 사용합니다.
 ERROR : 일반 에러가 일어 났을 때 사용합니다.
 WARN : 에러는 아니지만 주의할 필요가 있을 때 사용합니다.
 INFO : 일반 정보를 나타낼 때 사용합니다.
 DEBUG : 일반 정보를 상세히 나타낼 때 사용합니다.

ConversionPattern 사용 방법

 %p  debug, info, warn, error, fatal 등의 priority 가 출력된다.
 %m  로그내용이 출력됩니다
 %d  로깅 이벤트가 발생한 시간을 기록합니다.
   포맷은 %d{HH:mm:ss, SSS}, %d{yyyy MMM dd HH:mm:ss, SSS}같은 형태로 사용하며 SimpleDateFormat에 따른 포맷팅을 하면 된다
 %t  로그이벤트가 발생된 쓰레드의 이름을 출력합니다.
 %%  % 표시를 출력하기 위해 사용한다.
 %n  플랫폼 종속적인 개행문자가 출력된다. rn 또는 n 일것이다.
 %c  카테고리를 표시합니다
   예) 카테고리가 a.b.c 처럼 되어있다면 %c{2}는 b.c가 출력됩니다.
 %C  클래스명을 포시합니다.
   예)클래스구조가 org.apache.xyz.SomeClass 처럼 되어있다면 %C{2}는 xyz.SomeClass 가 출력됩니다
 %F  로깅이 발생한 프로그램 파일명을 나타냅니다.
 %l  로깅이 발생한 caller의 정보를 나타냅니다
 %L  로깅이 발생한 caller의 라인수를 나타냅니다
 %M  로깅이 발생한 method 이름을 나타냅니다.
 %r  어플리케이션 시작 이후 부터 로깅이 발생한 시점의 시간(milliseconds)
 %x  로깅이 발생한 thread와 관련된 NDC(nested diagnostic context)를 출력합니다.
 %X  로깅이 발생한 thread와 관련된 MDC(mapped diagnostic context)를 출력합니다.

log4j.properties 파일 사용 예

log4j.rootLogger=INFO, stdout, rolling

log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=[COTAMOT] %5p,[%d],[%t],(%F:%L),%m%n
log4j.appender.stdout.target=System.err

#rolling 어펜더는 파일로 처리한다라고 정의
 log4j.appender.rolling=org.apache.log4j.DailyRollingFileAppender
#로그 파일 이름은 output.log
 log4j.appender.rolling.File=output.log
#true면 WAS를 내렸다 올려도 파일이 리셋되지 않습니다.
 log4j.appender.rolling.Append=true
#파일 최대 사이즈는 500KB로 설정
 log4j.appender.rolling.DatePattern='.'yyyy-MM-dd
#rolling 어펜더는 패턴 레이아웃을 사용하겠다고 정의
 log4j.appender.rolling.layout=org.apache.log4j.PatternLayout
#rolling 어펜더는 패턴 레이아웃 포맷
 log4j.appender.rolling.layout.ConversionPattern=[COTAMOT] %5p,[%d],[%t],(%F:%L),%m%n

출처 : placebo4me.egloos.com
2008. 12. 12. 13:58

질문 sun.io.MalformedInputException에 대해서




네트웍 통신시 아래와 같은 에러가 발생합니다.
혹 이런 에러에 대해서 아시면 한수 가르침좀 부탁드립니다.
소스는 아래쪽에 써놓았습니다.

READ ERROR : null : sun.io.MalformedInputException
        at sun.io.ByteToCharDoubleByte.flush(ByteToCharDoubleByte.java:51)
        at java.io.InputStreamReader.flushInto(InputStreamReader.java:154)
        at java.io.InputStreamReader.fill(InputStreamReader.java:178)
        at java.io.InputStreamReader.read(InputStreamReader.java:249)
        at java.io.BufferedReader.fill(BufferedReader.java:139)
        at java.io.BufferedReader.readLine(BufferedReader.java:299)
        at java.io.BufferedReader.readLine(BufferedReader.java:363)
        at it21.acom_tcplib.RecvMsg_FromTcp(acom_tcplib.java:97)
        at incheon_segu_daily.Send_Message(incheon_segu_daily.java:273)
        at incheon_segu_daily.Get_Result(incheon_segu_daily.java:166)
        at incheon_segu_daily.service(incheon_segu_daily.java:142)
        at xecure.servlet.XecureHttpServlet.service(XecureHttpServlet.java:261)
        at javax.servlet.http.HttpServlet.service(HttpServlet.java:274)
        at jeus.servlet.engine.ServletWrapper.execSingleThreadServlet(ServletWrapper.java:222)
        at jeus.servlet.engine.ServletWrapper.execServlet(ServletWrapper.java:236)
        at jeus.servlet.engine.ApacheRequestProcessor.run(ApacheRequestProcessor.java:85)

아래 소스 main에서 보면 args[0]는 1인 경우에는 위와 같은 에러가 발생하고
1이 아닌 경우에는 제대로 데이터를 받습니다.
물론 위의 에러를 일으킨 소스는 웹쪽에서 서비스 되는 건데
아래의 소스와 같은 라이브러리를 사용합니다.

왜인지 아무리 생각해도 모르겠네요..
두 소스의 차이점은 args[0]는 1인 경우는 BufferedReader를 사용했고
아닌경우는 DataInputStream을 사용했다는 차임점인데 잘 모르겠습니다.

참고로 통신서버는 SUN 솔라리스이고 서블릿 엔진은 JEUS를 사용했습니다.

import java.io.*;
import java.net.*;
import java.util.*;
import java.text.*;
import java.util.Date;

public class TcpNet {
	public static void main(String args[]){
		Socket sock = null;
		String url = "210.112.177.2";
		String port="8300";
		String sendMsg = send();
		String recvMsg = null;
		int len = sendMsg.length();
		long timelimit=10000 ;

		System.out.println("SEND=["+sendMsg+"]");
		if( args[0].equals("1") ) {
			try{ 
				sock = connect( url, port );
				send1( sock, sendMsg, len, timelimit );
				recvMsg = read1( sock, timelimit );
			}catch(Exception e){
				e.printStackTrace();
			}finally{
				close(sock);
			}
		} else {
			try{
				sock = connect( url, port );
				send2( sock, sendMsg, len, timelimit );
				recvMsg = read2( sock, timelimit );
			}catch(Exception e){
				e.printStackTrace();
			}finally{
				close(sock);
			}
		}
		System.out.println("RECV=["+recvMsg+"]");
		System.exit(0);
	}

	public static String send() {
		//지방세 조회 EFFV
		String sendMsg = "TEMP20020209Y                      000000000000000000000000                                                                  0000000000000                000000000000000000                                                                  0000000000000                000000000000000000                                                                  0000000000000                000000000000000000                                                                  0000000000000                000000000000000000                                                                  0000000000000                000000000000000000                                                                  0000000000000                000000000000000000                        ";
		return sendMsg;
	}

	public static Socket connect(String url, String port) throws Exception {
		Socket client;
		//첫글자가 숫자면 ip 로처리
		if(Character.isDigit(url.charAt(0))) {
			client = new Socket(url, Integer.parseInt(port));
		}else{
       		InetAddress url_ = InetAddress.getByName(url);
       		client = new Socket(url_, Integer.parseInt(port));
		}
		return client;
	}

	public static void send1(Socket sock, String sendMsg, int len, long timelimit) {
		try {
		   sock.setTcpNoDelay(true);
           PrintWriter pw = new PrintWriter( new OutputStreamWriter( sock.getOutputStream() ), true );
           pw.print( sendMsg );
           pw.flush();
		} catch(Exception e) {
			close(sock);
			e.printStackTrace();
		}
	}

	public static void send2(Socket sock, String sendMsg, int len, long timelimit) {
		OutputStream pw = null;
		try {
			sock.setSoTimeout( (int)timelimit); 
			pw = sock.getOutputStream();
			byte[] b=sendMsg.getBytes();
			pw.write(b);
			pw.flush();
		}catch(Exception e){
			if(pw!=null){ try{ pw.close(); }catch(IOException ioe){} }
			close(sock);
			e.printStackTrace();
		}
	}

	public static String read1(Socket sock, long timelimit){
        String recvMsg = "";
		BufferedReader br=null;
		try {
			sock.setSoTimeout( (int)timelimit );   // 수신제한시간 설정 milli sec ( 1분 )
			br = new BufferedReader( new InputStreamReader( sock.getInputStream() ) );
            String line = null;
			int i = 0;
            while( ( line = br.readLine() ) != null ) {
              recvMsg += line;
            }
            br.close();
		} catch(Exception e) {
			close(sock);
			e.printStackTrace();
		}
	
		return recvMsg;
	}

	public static String read2(Socket sock, long timelimit){
        String recvMsg = "";
		DataInputStream in = null; 
		byte[] recvData = new byte[1024];

		try {
			sock.setSoTimeout( (int)timelimit); 	
			in = new DataInputStream(sock.getInputStream() );

			int i = 0, start_pos = 0;
			i = in.read( recvData );

			recvMsg = new String(recvData);

		}catch(Exception e){
			e.printStackTrace();
			return null;
		}finally{
			if(in!=null){ try{ in.close(); }catch(IOException ioe){} }
			if(sock!=null){ try{ sock.close(); }catch(IOException ioe){} }
		}
		return recvMsg;
	}

	public static void close(Socket client) {
		try {
			client.close();
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
}
제목 : Re: 질문 sun.io.MalformedInputException에 대해서
글쓴이: 김완석(kinyang) 2002/02/19 10:15:11 조회수:1761 줄수:66
>
> {질문 sun.io.MalformedInputException에 대해서}
> {손님(guest), lsk7up@hanmail.net}
>
> 네트웍 통신시 아래와 같은 에러가 발생합니다.
> 혹 이런 에러에 대해서 아시면 한수 가르침좀 부탁드립니다.
> 소스는 아래쪽에 써놓았습니다.
> 
> READ ERROR : null : sun.io.MalformedInputException
>         at sun.io.ByteToCharDoubleByte.flush(ByteToCharDoubleByte.java:51)
>         at java.io.InputStreamReader.flushInto(InputStreamReader.java:154)
>         at java.io.InputStreamReader.fill(InputStreamReader.java:178)
>         at java.io.InputStreamReader.read(InputStreamReader.java:249)
>         at java.io.BufferedReader.fill(BufferedReader.java:139)
>         at java.io.BufferedReader.readLine(BufferedReader.java:299)
>         at java.io.BufferedReader.readLine(BufferedReader.java:363)
>         at it21.acom_tcplib.RecvMsg_FromTcp(acom_tcplib.java:97)
>         at incheon_segu_daily.Send_Message(incheon_segu_daily.java:273)
>         at incheon_segu_daily.Get_Result(incheon_segu_daily.java:166)
>         at incheon_segu_daily.service(incheon_segu_daily.java:142)
>         at xecure.servlet.XecureHttpServlet.service(XecureHttpServlet.java:261)
>         at javax.servlet.http.HttpServlet.service(HttpServlet.java:274)
>         at jeus.servlet.engine.ServletWrapper.execSingleThreadServlet(ServletWrapper.java:222)
>         at jeus.servlet.engine.ServletWrapper.execServlet(ServletWrapper.java:236)
>         at jeus.servlet.engine.ApacheRequestProcessor.run(ApacheRequestProcessor.java:85)
> 
> 아래 소스 main에서 보면 args[0]는 1인 경우에는 위와 같은 에러가 발생하고
> 1이 아닌 경우에는 제대로 데이터를 받습니다.
> 물론 위의 에러를 일으킨 소스는 웹쪽에서 서비스 되는 건데
> 아래의 소스와 같은 라이브러리를 사용합니다.
> 
> 왜인지 아무리 생각해도 모르겠네요..
> 두 소스의 차이점은 args[0]는 1인 경우는 BufferedReader를 사용했고
> 아닌경우는 DataInputStream을 사용했다는 차임점인데 잘 모르겠습니다.
> 

 MalformedInputException 을 구지 해석하자면 ...

 잘못된 형식의 입력으로 error 발생한 것입니다.

 문제의 원인은 Stream의 끝에서 에러가 발생한 듯 싶습니다.

 DataInputStream 을 사용했을 때에는 문제가 없고 BufferedReader 를 사용했을때
 
 문제가 발생하는것은..

 DataInputStream 는 한바이트씩 읽어서 처리하지만,

 BufferedReader 는 2바이트를 읽어 하나의 char로 변환합니다.
 
 위의 MalformedInputException 은 이 과정에서 발생한 듯 싶습니다.
 
 한 바이트를 읽고 난 다음에 다음 바이르틀 읽어 하나의 Char를 만들려다가 
 실패한 것이지요 ...

 제가 소스를 전체적으로 살펴본것은 아니지만, 

 네트웍 상에서는 데이터의 전송을 \r\n으로 구분지어서 전송합니다 ....

 전송되는 데이터의 끝에 \r\n을 추가하여 전송해 보세요 ...

 음냥... 

 그럼 수거하세요 ...

출처 : javasevice.net