이 문서는 가리사니 개발자 포럼에 올렸던 글의 백업 파일입니다. 오래된 문서가 많아 현재 상황과 맞지 않을 수 있습니다.
XML-RPC 블로그 시리즈
Apache XML-RPC 를 이용하는 방법
- 사실 너무많은 파일이 필요해서 필자는 직접 구현을 더 추천합니다.
- 직접구현은 아래에 있습니다.
- 클라이언트 기능만 쓰지만, 서로 연관된것들이 많아 추가 후 필요없는 기능들을 제거하시면 될 것 같습니다. pom.xml 추가 ``` xml
글 등록 구현 예제
``` 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 참고해보세요.
- 다만 위 사이트 소스는 의도된건지 버그인지 모르겠지만 < 가 있으면 < -> < 로 연속으로 분해됨으로,
- & 를 먼저 처리하게 수정하시기 바랍니다. 그냥 이소스 그대로 쓰셔도됩니다…. ``` 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(“
”); return this; }"); encodeXmlEscape(data, name); data.write(" <"); data.write(type); data.write('>'); encodeXmlEscape(data, value); data.write("</"); data.write(type); data.write(">
/**
- 맴버추가
- @param name
- @param value
- @return
- @throws IOException
*/
public XmlRpcStruct addMember(String name, String value) throws IOException
{
data.write(“
”); return this; }"); encodeXmlEscape(data, name); data.write(" "); encodeXmlEscape(data, value); data.write("
/**
- 맴버추가
- @param name
- @param type
- @param value
- @return
- @throws IOException
*/
public XmlRpcStruct addMemberArray(String name, String type, String… values) throws IOException
{
data.write(“
”); return this; }"); 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("
/**
- 맴버추가
- @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 주소”);
- 최종 결과 xml을 가져옵니다.
- 연결을 합니다.
// 글쓰기 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>