원본 본문으로 이동하기

XML-RPC 블로그 : 5. JAVA로 구현해보자

박용서 - XML-RPC 블로그 시리즈 XML-RPC 블로그 : 1. XML-RPC 소개 - https://gs.saro.me/#!m=elec&jn=758 XML-RPC 블로그 : 2. 포스트 등록 - https://gs.saro.me/#!m=elec&jn=759 XML-RPC 블로그 : 3. 포스트 수정 - https://gs.saro.me/#!m=elec&jn=760 XML-RPC 블로그 : 4. 포스트 삭제 - https://gs.saro.me/#!m=elec&jn=761 XML-RPC 블로그 : 5. JAVA로 구현해보자 - https://gs.saro.me/#!m=elec&jn=762 Apache XML-RPC 를 이용하는 방법 - 사실 너무많은 파일이 필요해서 필자는 직접 구현을 더 추천합니다. - 직접구현은 아래에 있습니다. - 클라이언트 기능만 쓰지만, 서로 연관된것들이 많아 추가 후 필요없는 기능들을 제거하시면 될 것 같습니다. pom.xml 추가 <dependency> <groupId>org.apache.xmlrpc</groupId> <artifactId>xmlrpc-common</artifactId> <version>3.1.3</version> </dependency> <dependency> <groupId>org.apache.xmlrpc</groupId> <artifactId>xmlrpc-client</artifactId> <version>3.1.3</version> </dependency> <dependency> <groupId>org.apache.xmlrpc</groupId> <artifactId>xmlrpc-server</artifactId> <version>3.1.3</version> </dependency> <dependency> <groupId>org.apache.ws.commons.util</groupId> <artifactId>ws-commons-util</artifactId> <version>1.0.2</version> </dependency> <dependency> <groupId>commons-logging</groupId> <artifactId>commons-logging</artifactId> <version>1.1.1</version> </dependency> 글 등록 구현 예제 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; 로 연속으로 분해됨으로, - & 를 먼저 처리하게 수정하시기 바랍니다. 그냥 이소스 그대로 쓰셔도됩니다.... 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<br> * 2016-07-03 전명 박용서 : 작성<br> * 급하게 짠거라 리팩토링해서 쓰세요.. */ public class XmlRpcClient { /** 커넥션 */ private URLConnection connection; /** 요청 */ private Writer request; /** 생성자 : newInstance 사용 */ private XmlRpcClient() {} /** * 연결을 합니다.<br> * 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("<methodCall><methodName>"); xmlRpcClient.request.write(method); xmlRpcClient.request.write("</methodName><params>"); 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("<param><value><"); request.write(type); request.write('>'); encodeXmlEscape(request, value); request.write("</"); request.write(type); request.write("></value></param>"); return this; } /** * 파라미터를 추가합니다.<br> * 별도 데이터 타입을 넣지 않습니다. * @param value * @return * @throws IOException */ public XmlRpcClient addParam(String value) throws IOException { request.write("<param><value>"); encodeXmlEscape(request, value); request.write("</value></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("<param><value>"); struct.out(request); request.write("</value></param>"); return this; } /** * 최종 결과를 가져옵니다.<br> * <b>주의 : </b><br> * XmlRpcClient 객체는 send 를 부름으로써 끝나게 됩니다.<br> * 즉, 이 객체를 다시 재활용할 수 없습니다.<br> * 새롭게 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("<struct>"); out.write(((StringWriter)data).toString()); out.write("</struct>"); } /** * 맴버추가 * @param name * @param type * @param value * @return * @throws IOException */ public XmlRpcStruct addMember(String name, String type, String value) throws IOException { data.write("<member><name>"); encodeXmlEscape(data, name); data.write("</name><value><"); data.write(type); data.write('>'); encodeXmlEscape(data, value); data.write("</"); data.write(type); data.write("></value></member>"); return this; } /** * 맴버추가 * @param name * @param value * @return * @throws IOException */ public XmlRpcStruct addMember(String name, String value) throws IOException { data.write("<member><name>"); encodeXmlEscape(data, name); data.write("</name><value>"); encodeXmlEscape(data, value); data.write("</value></member>"); return this; } /** * 맴버추가 * @param name * @param type * @param value * @return * @throws IOException */ public XmlRpcStruct addMemberArray(String name, String type, String... values) throws IOException { data.write("<member><name>"); encodeXmlEscape(data, name); data.write("</name><value><array><data>"); for (String value : values) { data.write("<value><"); data.write(type); data.write('>'); encodeXmlEscape(data, value); data.write("</"); data.write(type); data.write("></value>"); } data.write("</data></array></value></member>"); 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을 가져옵니다.<br> * 나중에 dom을 복사하지못하도록 방어적 복사복 추가 바람. * @return */ public Document getResultDocument() { return this.dom; } /** * 성공여부를 가져옵니다. * @return */ public boolean isSuccess() { return success; } /** * 성공 결과를 가져옵니다.<br> * 성공하지 못한경우는 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; } /** * 스트링 출력<br> * 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(); } } } 사용하기 // 각종정보 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()); } 성공했다면 아래와 같은 출력이 나옵니다. 성공 [[포스트 번호]] <?xml version="1.0" encoding="utf-8" standalone="no"?> <methodResponse> <params> <param> <value>[[포스트 번호]]</value> </param> </params> </methodResponse> - XML/XSL/XSLT 자바 프로토콜