package com.redhat.ceylon.cmr.util; import java.io.IOException; import java.io.InputStream; import java.io.UnsupportedEncodingException; import java.net.HttpURLConnection; import java.net.Proxy; import java.net.URL; import java.net.URLEncoder; import java.util.Arrays; import java.util.HashMap; import java.util.LinkedList; import java.util.List; import java.util.Map; import javax.xml.stream.XMLInputFactory; import javax.xml.stream.XMLStreamException; import javax.xml.stream.XMLStreamReader; import com.redhat.ceylon.common.Constants; import com.redhat.ceylon.common.config.DefaultToolOptions; /** * WS REST client for XML * * @author Stéphane Épardaud <stef@epardaud.fr> */ public class WS { public static class Parser { private XMLStreamReader reader; public Parser(XMLStreamReader reader) { this.reader = reader; } public boolean moveToOptionalOpenTag(String name){ nextTag(); return isOpenTag(name); } public boolean moveToOptionalOpenTag() { nextTag(); return isOpenTag(); } public String getAttribute(String name){ return reader.getAttributeValue(null, name); } public boolean isOpenTag() { int code = reader.getEventType(); return code == XMLStreamReader.START_ELEMENT; } public boolean isOpenTag(String name) { return (isOpenTag() && tagName().equals(name)); } public void moveToOpenTag(String name) { nextTag(); if(!isOpenTag(name)) throw new RuntimeException("Expected open tag "+name+" but got "+reader.getEventType()); } public void checkCloseTag() { int code = reader.getEventType(); if(code != XMLStreamReader.END_ELEMENT) throw new RuntimeException("Expected close tag but got "+code); } public void nextTag(){ try { reader.nextTag(); } catch (XMLStreamException e) { throw new RuntimeException(e); } } public String contents() { try { String contents = reader.getElementText(); return contents; } catch (XMLStreamException e) { throw new RuntimeException(e); } } public String tagName() { return reader.getName().getLocalPart(); } } public interface XMLHandler { public void onOK(Parser parser); } public static void getXML(String url, Param[] params, XMLHandler handler){ if(params != null) url += toQueryString(Arrays.asList(params)); getXML(url, handler); } public static void getXML(String url, List<Param> params, XMLHandler handler){ if(params != null) url += toQueryString(params); getXML(url, handler); } public static void getXML(String url, XMLHandler handler){ try{ URL endpoint = new URL(url); HttpURLConnection connection; Proxy proxy = DefaultToolOptions.getDefaultProxy(); if (proxy != null) { connection = (HttpURLConnection) endpoint.openConnection(proxy); } else { connection = (HttpURLConnection) endpoint.openConnection(); } connection.setConnectTimeout((int) DefaultToolOptions.getDefaultTimeout()); connection.setReadTimeout((int) DefaultToolOptions.getDefaultTimeout() * Constants.READ_TIMEOUT_MULTIPLIER); getXML(connection, handler); }catch(IOException x){ throw new RuntimeException(x); } } public static void getXML(HttpURLConnection connection, XMLHandler handler){ try{ connection.addRequestProperty("Accept", "application/xml"); connection.connect(); try{ if(connection.getResponseCode() == 200){ InputStream is = connection.getInputStream(); try{ XMLInputFactory factory = XMLInputFactory.newFactory(); XMLStreamReader reader = factory.createXMLStreamReader(is); try{ Parser p = new Parser(reader); handler.onOK(p); }finally{ reader.close(); } }finally{ is.close(); } } }finally{ connection.disconnect(); } }catch(XMLStreamException x){ throw new RuntimeException(x); }catch(IOException x){ throw new RuntimeException(x); } } public static List<Link> collectLinks(HttpURLConnection con) { List<String> linkHeaders = con.getHeaderFields().get("Link"); List<Link> ret = new LinkedList<Link>(); if (linkHeaders != null) { for(String linkHeader : linkHeaders){ // split it at "," String[] links = linkHeader.split(","); for(String link : links){ ret.add(parseLink(link)); } } } return ret; } public static String getLink(List<Link> links, String rel){ for (Link link : links) { if(rel.equals(link.params.get("rel"))) return link.url; } return null; } public static class Link { public String url; public Map<String, String> params = new HashMap<String, String>(); public Link(String url) { this.url = url; } } private static Link parseLink(String link) { // <url> (; name = val)* String[] parts = link.split(";"); // first part will be the url String url = parts[0].trim(); if(!url.startsWith("<") || !url.endsWith(">")) throw new RuntimeException("Invalid link header: "+link); url = url.substring(1, url.length()-1); if(url.isEmpty()) throw new RuntimeException("Invalid link header: "+link); Link ret = new Link(url); for(int i=1;i<parts.length;i++){ // split at = String part = parts[i]; int eq = part.indexOf('='); if(eq == -1) throw new RuntimeException("Invalid link header: "+link); String name = part.substring(0, eq).trim(); String val = part.substring(eq+1).trim(); if(name.isEmpty() || val.isEmpty()) throw new RuntimeException("Invalid link header: "+link); if(val.startsWith("\"")){ if(!val.endsWith("\"")) throw new RuntimeException("Invalid link header: "+link); // no trimming: it's quoted val = val.substring(1, val.length()-1); if(val.isEmpty()) throw new RuntimeException("Invalid link header: "+link); } ret.params.put(name, val); } return ret; } public static class Param{ public String name; public String[] values; public Param(String name, String[] values) { this.name = name; this.values = values; } public Param(String name, Long value) { this.name = name; this.values = new String[1]; this.values[0] = value != null ? value.toString() : null; } public Param(String name, Integer value) { this.name = name; this.values = new String[1]; this.values[0] = value != null ? value.toString() : null; } public Param(String name, Boolean value) { this.name = name; this.values = new String[1]; this.values[0] = value != null ? value.toString() : null; } public void toString(StringBuilder b) { if(values.length == 1){ b.append(encodeURLQueryParam(name)).append("=").append(encodeURLQueryParam(values[0])); return; } if(values.length == 0){ b.append(encodeURLQueryParam(name)).append("="); } for (int i = 0; i < values.length; i++) { if(i != 0) b.append("&"); b.append(encodeURLQueryParam(name)).append("=").append(encodeURLQueryParam(values[i])); } } } public static Param param(String name, String... values) { return new Param(name, values); } public static Param param(String name, Long value) { return new Param(name, value); } public static Param param(String name, Integer value) { return new Param(name, value); } public static Param param(String name, Boolean value) { return new Param(name, value); } private static String encodeURLQueryParam(String name) { if(name == null) return ""; // this encoding is good enough for query param parts try { return URLEncoder.encode(name, "UTF-8"); } catch (UnsupportedEncodingException e) { // can't happen throw new RuntimeException(e); } } private static String toQueryString(List<Param> params){ StringBuilder b = new StringBuilder("?"); boolean first = true; for (Param param : params) { if (!first) { b.append("&"); } param.toString(b); first = false; } return b.toString(); } public static Param[] params(Param... params) { return params; } }