[백업][가리사니] xml Rpc 블로그 : 5. java로 구현해보자
java, protocols, xml

이 문서는 가리사니 개발자 포럼에 올렸던 글의 백업 파일입니다. 오래된 문서가 많아 현재 상황과 맞지 않을 수 있습니다.

XML-RPC 블로그 시리즈

Apache XML-RPC 를 이용하는 방법

  • 사실 너무많은 파일이 필요해서 필자는 직접 구현을 더 추천합니다.
  • 직접구현은 아래에 있습니다.
  • 클라이언트 기능만 쓰지만, 서로 연관된것들이 많아 추가 후 필요없는 기능들을 제거하시면 될 것 같습니다. pom.xml 추가 ``` xml
org.apache.xmlrpc xmlrpc-common 3.1.3 org.apache.xmlrpc xmlrpc-client 3.1.3 org.apache.xmlrpc xmlrpc-server 3.1.3 org.apache.ws.commons.util ws-commons-util 1.0.2 commons-logging commons-logging 1.1.1
글 등록 구현 예제
``` java
XmlRpcClient blog  = new XmlRpcClient();
XmlRpcClientConfigImpl config = new XmlRpcClientConfigImpl();
Hashtable<String, String> post = new Hashtable<>();
Vector<Serializable> params = new Vector<Serializable>();

// 예를들어 네이버 기준으로 https://api.blog.naver.com/xmlrpc
config.setServerURL(new URL("XML RPC 주소"));
config.setBasicEncoding("utf-8");
config.setEncoding("utf-8");

// 포스트 등록글 참고바람.
// 네이버 같은경우 블로그 ID나 서비스 ID나 그냥 ID 입니다.
params.addElement("블로그 ID");
params.addElement("서비스 ID");
params.addElement("API 키");

// 각종 컨텐츠 구조체(포스트 등록 참고)
post.put("title", "타이틀");
post.put("description", "컨텐츠<b>asdf</b>asdf");
post.put("tags", "가리사니,개발자,공간");
post.put("categories", "프로그래밍");
params.addElement(post);

// 숨김여부
params.addElement(true);

// 성공시 포스트 ID가 리턴됩니다.
// 실패시 Exception 으로 오류를 알려줍니다.
String blogPostID = (String) blog.execute(config, "metaWeblog.newPost", params);

직접구현

XML RPC 클라이언트 객체

  • 급하게 대충짠거라 소스가 엉망입니다. 하하하..;;
  • 인코딩은 [http://www.htmlescape.net/htmlescape_for_java.html 참고해보세요.
  • 다만 위 사이트 소스는 의도된건지 버그인지 모르겠지만 < 가 있으면 < -> &lt; 로 연속으로 분해됨으로,
  • & 를 먼저 처리하게 수정하시기 바랍니다. 그냥 이소스 그대로 쓰셔도됩니다…. ``` java import java.io.IOException; import java.io.InputStream; import java.io.OutputStreamWriter; import java.io.StringWriter; import java.io.Writer; import java.net.HttpURLConnection; import java.net.URL; import java.net.URLConnection; import java.util.HashMap; import java.util.Map;

import javax.net.ssl.HttpsURLConnection; import javax.net.ssl.SSLSocketFactory; import javax.xml.parsers.DocumentBuilderFactory; import javax.xml.parsers.ParserConfigurationException; import javax.xml.transform.OutputKeys; import javax.xml.transform.Transformer; import javax.xml.transform.TransformerException; import javax.xml.transform.TransformerFactory; import javax.xml.transform.TransformerFactoryConfigurationError; import javax.xml.transform.dom.DOMSource; import javax.xml.transform.stream.StreamResult;

import org.w3c.dom.Document; import org.w3c.dom.Node; import org.w3c.dom.NodeList; import org.xml.sax.SAXException;

/**

  • XmlRpcClient
  • 2016-07-03 전명 박용서 : 작성
  • 급하게 짠거라 리팩토링해서 쓰세요.. */ public class XmlRpcClient { /** 커넥션 */ private URLConnection connection; /** 요청 */ private Writer request;

    /** 생성자 : newInstance 사용 */ private XmlRpcClient() {}

    /**

    • 연결을 합니다.
    • SSL/TLS 인증에 문제가 있는 경우에만 여기서 수동으로 사용하길 바라며,
    • 그렇지않은경우 newInstance(URL url, String method) 를 사용하기 바랍니다.
    • @param url
    • @param sslSocketFactory
    • @param method
    • @return
    • @throws IOException */ public static XmlRpcClient newInstance(URL url, SSLSocketFactory sslSocketFactory, String method) throws IOException { // 연결 HttpURLConnection connection = (HttpURLConnection) url.openConnection();

      // 커스텀 보안인증 if (sslSocketFactory != null && “https”.equals(url.getProtocol())) { ((HttpsURLConnection)connection).setSSLSocketFactory(sslSocketFactory); }

      connection.setRequestMethod(“POST”); connection.setDoOutput(true); connection.setDoInput(true); // 헤더들을 지워주지 않으면 서버에 따라서 인식을 못함. connection.setRequestProperty(“User-Agent”, “”); connection.setRequestProperty(“Accept”, “”); connection.setRequestProperty(“Content-type”, “”); connection.connect();

      // 연결 XmlRpcClient xmlRpcClient = new XmlRpcClient(); xmlRpcClient.connection = connection; xmlRpcClient.request = new OutputStreamWriter(connection.getOutputStream(), “utf-8”);

      // 기본문입력 xmlRpcClient.request.write(“"); xmlRpcClient.request.write(method); xmlRpcClient.request.write("");

      return xmlRpcClient; }

    /**

    • 연결을 합니다.
    • @param url
    • @param method
    • @return
    • @throws IOException */ public static XmlRpcClient newInstance(URL url, String method) throws IOException { return newInstance(url, null, method); }

    /**

    • XML 이스케이프
    • @param out
    • @param value
    • @throws IOException */ private static void encodeXmlEscape(Writer out, String value) throws IOException { char[] chars = value.toCharArray(); for (char c : chars) { switch (c) { case ‘&’ : out.write(“&”); break; case ‘<’ : out.write(“<”); break; case ‘>’ : out.write(“>”); break; default : out.write(c); } } }

    /**

    • 타입을 지정하는 파라미터를 추가합니다.
    • @param type
    • @param value
    • @return
    • @throws IOException */ public XmlRpcClient addParam(String type, String value) throws IOException { request.write(“<"); request.write(type); request.write('>'); encodeXmlEscape(request, value); request.write("</"); request.write(type); request.write("></param>”); return this; }

    /**

    • 파라미터를 추가합니다.
    • 별도 데이터 타입을 넣지 않습니다.
    • @param value
    • @return
    • @throws IOException */ public XmlRpcClient addParam(String value) throws IOException { request.write(“"); encodeXmlEscape(request, value); request.write("</param>”); return this; }

    /**

    • string 타입의 파라미터를 추가합니다.
    • @param value
    • @return
    • @throws IOException */ public XmlRpcClient addParamString(String value) throws IOException { return addParam(“string”, value); }

    /**

    • int 타입의 파라미터를 추가합니다.
    • @param value
    • @return
    • @throws IOException */ public XmlRpcClient addParamInt(int value) throws IOException { return addParam(“int”, Integer.toString(value)); }

    /**

    • boolean 타입의 파라미터를 추가합니다.
    • @param value
    • @return
    • @throws IOException */ public XmlRpcClient addParamBoolean(boolean value) throws IOException { return addParam(“boolean”, value ? “1” : “0”); }

    /**

    • struct 타입의 파라미터를 추가합니다.
    • @param struct
    • @return
    • @throws IOException */ public XmlRpcClient addParamStruct(XmlRpcStruct struct) throws IOException { request.write(“"); struct.out(request); request.write("</param>”); return this; }

    /**

    • 최종 결과를 가져옵니다.
    • 주의 :
    • XmlRpcClient 객체는 send 를 부름으로써 끝나게 됩니다.
    • 즉, 이 객체를 다시 재활용할 수 없습니다.
    • 새롭게 newInstance 하시기 바랍니다.
    • @return
    • @throws IOException
    • @throws SAXException
    • @throws ParserConfigurationException */ public XmlRpcResult send() throws IOException, SAXException, ParserConfigurationException { // 보내기 끝 / 닫기 request.write(“</params></methodCall>”); request.flush(); request.close();

      // 반환 try (InputStream is = connection.getInputStream()) { return new XmlRpcResult(DocumentBuilderFactory.newInstance().newDocumentBuilder().parse(is)); } catch (IOException e) { throw e; } }

    /**

    • XmlRpcStruct 객체 */ public static class XmlRpcStruct { private Writer data = new StringWriter();

      /**

      • 만들어진 결과를 wirter 를 통해 출력합니다.
      • @param writer
      • @throws IOException */ private void out(Writer out) throws IOException { out.write(“"); out.write(((StringWriter)data).toString()); out.write("”); }

      /**

      • 맴버추가
      • @param name
      • @param type
      • @param value
      • @return
      • @throws IOException */ public XmlRpcStruct addMember(String name, String type, String value) throws IOException { data.write(“"); encodeXmlEscape(data, name); data.write("<"); data.write(type); data.write('>'); encodeXmlEscape(data, value); data.write("</"); data.write(type); data.write(">”); return this; }

      /**

      • 맴버추가
      • @param name
      • @param value
      • @return
      • @throws IOException */ public XmlRpcStruct addMember(String name, String value) throws IOException { data.write(“"); encodeXmlEscape(data, name); data.write(""); encodeXmlEscape(data, value); data.write("”); return this; }

      /**

      • 맴버추가
      • @param name
      • @param type
      • @param value
      • @return
      • @throws IOException */ public XmlRpcStruct addMemberArray(String name, String type, String… values) throws IOException { data.write(“"); encodeXmlEscape(data, name); data.write(""); for (String value : values) { data.write("<"); data.write(type); data.write('>'); encodeXmlEscape(data, value); data.write("</"); data.write(type); data.write(">"); } data.write("”); return this; }

      /**

      • 맴버추가
      • @param name
      • @param value
      • @return
      • @throws IOException */ public XmlRpcStruct addMemberString(String name, String value) throws IOException { return addMember(name, “string”, value); }

      /**

      • 맴버추가
      • @param name
      • @param value
      • @return
      • @throws IOException */ public XmlRpcStruct addMemberStringArray(String name, String… value) throws IOException { return addMemberArray(name, “string”, value); } }

    /**

    • XmlRpcResult */ public static class XmlRpcResult { private final Document dom; private final boolean success; private String result; private Map<String, String> fault;

      /** 직접 생성 불가 */ private XmlRpcResult(Document dom) { this.dom = dom;

       // 성공
       if ( (this.success = (this.dom.getElementsByTagName("fault").getLength() == 0)) )
       {
           NodeList nl = this.dom.getElementsByTagName("value");
           if (nl.getLength() != 0)
           {
               Node node = nl.item(0);
               if (node.hasChildNodes())
               {
                   result = node.getChildNodes().item(0).getTextContent();
               }
               else
               {
                   result = node.getTextContent();
               }
           }
       }
       // 실패
       else
       {
           NodeList nl = this.dom.getElementsByTagName("member");
           fault = new HashMap<>();
           for (int i = 0 ; i < nl.getLength() ; i++)
           {
               Node node = nl.item(i);
               NodeList member = node.getChildNodes();
               String name = null, value = null;
      
               for (int j = 0 ; j < member.getLength() ; j++)
               {
                   Node attr = member.item(j);
                   switch (attr.getNodeName())
                   {
                       case "name" : name = attr.getTextContent(); break;
                       case "value" : value = attr.getTextContent(); break;
                   }
               }
      
               if (name != null && value != null)
               {
                   fault.put(name.toLowerCase(), value);
               }
           }
      
       }  }
      

      /**

      • 최종 결과 xml을 가져옵니다.
      • 나중에 dom을 복사하지못하도록 방어적 복사복 추가 바람.
      • @return */ public Document getResultDocument() { return this.dom; }

      /**

      • 성공여부를 가져옵니다.
      • @return */ public boolean isSuccess() { return success; }

      /**

      • 성공 결과를 가져옵니다.
      • 성공하지 못한경우는 null이 반환됩니다.
      • @return */ public String getResult() { return result; }

      /**

      • 실패사유를 가져옵니다.
      • @return */ public String getFaultString() { if (!isSuccess()) { return fault.get(“faultstring”); } return null; }

      /**

      • 실패 코드를 가져옵니다.
      • @return */ public String getFaultCode() { if (!isSuccess()) { return fault.get(“faultcode”); } return null; }

      /**

      • 스트링 출력
      • toString을 오버라이드 하지 않는 이유는
      • try - catch를 예외 처리하는 것보다 별도로 작성하는게 낫다고 생각.
      • @throws TransformerFactoryConfigurationError
      • @throws TransformerException */ public String getResultDocumentString() throws TransformerFactoryConfigurationError, TransformerException { Transformer transformer = TransformerFactory.newInstance().newTransformer(); transformer.setOutputProperty(OutputKeys.INDENT, “yes”); StreamResult result = new StreamResult(new StringWriter()); DOMSource source = new DOMSource(this.dom); transformer.transform(source, result); return result.getWriter().toString(); } } } 사용하기 java // 각종정보 String id = “블로그 ID”; String key = “API 키”; URL url = new URL(“XML RPC 주소”);

// 글쓰기 XmlRpcClient blog = XmlRpcClient.newInstance(url, “metaWeblog.newPost”); blog.addParamString(id); blog.addParamString(id); blog.addParamString(key); blog.addParamStruct ( new XmlRpcStruct() .addMemberString(“title”, “가리사니 개발자공간”) .addMemberStringArray(“categories”, “프로그래밍”) .addMemberString(“description”, “<h1>내용입니다.</h1>가나다라”) // 태그 : 네이버 .addMemberString(“tags”, “태그1, 태그2”) // 태그 : 티스토리 .addMemberStringArray(“mt_keywords”, “태그1”, “태그2”) ); blog.addParamBoolean(true); XmlRpcResult result = blog.send();

if (result.isSuccess()) { System.out.println(“성공”); System.out.println(result.getResult()); System.out.println(result.getResultDocumentString()); } else { System.out.println(“실패”); System.out.println(result.getFaultCode()); System.out.println(result.getFaultString()); System.out.println(result.getResultDocumentString()); }

성공했다면 아래와 같은 출력이 나옵니다.
``` shell
성공
[[포스트 번호]]
<?xml version="1.0" encoding="utf-8" standalone="no"?>
<methodResponse>
<params>
<param>
<value>[[포스트 번호]]</value>
</param>
</params>
</methodResponse>