1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 package org.jclouds.http.functions;
20
21 import static com.google.common.base.Preconditions.checkArgument;
22 import static com.google.common.base.Preconditions.checkNotNull;
23 import static com.google.common.io.Closeables.closeQuietly;
24
25 import java.io.IOException;
26 import java.io.InputStream;
27 import java.io.StringReader;
28
29 import javax.inject.Inject;
30
31 import org.jclouds.http.HttpRequest;
32 import org.jclouds.http.HttpResponse;
33 import org.jclouds.rest.InvocationContext;
34 import org.jclouds.rest.internal.GeneratedHttpRequest;
35 import org.jclouds.util.Strings2;
36 import org.xml.sax.InputSource;
37 import org.xml.sax.SAXException;
38 import org.xml.sax.SAXParseException;
39 import org.xml.sax.XMLReader;
40 import org.xml.sax.helpers.DefaultHandler;
41
42 import com.google.common.base.Function;
43 import com.google.common.base.Throwables;
44
45
46
47
48
49
50
51 public class ParseSax<T> implements Function<HttpResponse, T>, InvocationContext<ParseSax<T>> {
52
53 private final XMLReader parser;
54 private final HandlerWithResult<T> handler;
55 private HttpRequest request;
56
57 public static interface Factory {
58 <T> ParseSax<T> create(HandlerWithResult<T> handler);
59 }
60
61 @Inject
62 public ParseSax(XMLReader parser, HandlerWithResult<T> handler) {
63 this.parser = checkNotNull(parser, "parser");
64 this.handler = checkNotNull(handler, "handler");
65 }
66
67 public T apply(HttpResponse from) {
68 try {
69 checkNotNull(from, "http response");
70 checkNotNull(from.getPayload(), "payload in " + from);
71 } catch (NullPointerException e) {
72 return addDetailsAndPropagate(from, e);
73 }
74 if (from.getStatusCode() >= 300)
75 return convertStreamToStringAndParse(from);
76 InputStream is = from.getPayload().getInput();
77 try {
78 return parse(new InputSource(is));
79 } catch (RuntimeException e) {
80 return addDetailsAndPropagate(from, e);
81 } finally {
82 closeQuietly(is);
83 }
84 }
85
86 private T convertStreamToStringAndParse(HttpResponse from) {
87 try {
88 return parse(Strings2.toStringAndClose(from.getPayload().getInput()));
89 } catch (Exception e) {
90 return addDetailsAndPropagate(from, e);
91 }
92 }
93
94 public T parse(String from) {
95 try {
96 checkNotNull(from, "xml string");
97 checkArgument(from.indexOf('<') >= 0, String.format("not an xml document [%s] ", from));
98 } catch (RuntimeException e) {
99 return addDetailsAndPropagate(null, e);
100 }
101 return parse(new InputSource(new StringReader(from)));
102 }
103
104 public T parse(InputStream from) {
105 try {
106 return parse(new InputSource(from));
107 } finally {
108 closeQuietly(from);
109 }
110 }
111
112 public T parse(InputSource from) {
113 try {
114 return doParse(from);
115 } catch (Exception e) {
116 return addDetailsAndPropagate(null, e);
117 }
118 }
119
120 protected T doParse(InputSource from) throws IOException, SAXException {
121 checkNotNull(from, "xml inputsource");
122 from.setEncoding("UTF-8");
123 parser.setContentHandler(getHandler());
124
125 parser.parse(from);
126 return getHandler().getResult();
127 }
128
129 public T addDetailsAndPropagate(HttpResponse response, Exception e) {
130 StringBuilder message = new StringBuilder();
131 if (request != null) {
132 message.append("request: ").append(request.getRequestLine());
133 }
134 if (response != null) {
135 if (message.length() != 0)
136 message.append("; ");
137 message.append("response: ").append(response.getStatusLine());
138 }
139 if (e instanceof SAXParseException) {
140 SAXParseException parseException = (SAXParseException) e;
141 String systemId = parseException.getSystemId();
142 if (systemId == null) {
143 systemId = "";
144 }
145 if (message.length() != 0)
146 message.append("; ");
147 message.append(String.format("error at %d:%d in document %s", parseException.getColumnNumber(),
148 parseException.getLineNumber(), systemId));
149 }
150 if (message.length() != 0) {
151 message.append("; cause: ").append(e.toString());
152 throw new RuntimeException(message.toString(), e);
153 } else {
154 Throwables.propagate(e);
155 return null;
156 }
157
158 }
159
160 public HandlerWithResult<T> getHandler() {
161 return handler;
162 }
163
164
165
166
167
168
169 public abstract static class HandlerWithResult<T> extends DefaultHandler implements
170 InvocationContext<HandlerWithResult<T>> {
171 private HttpRequest request;
172
173 protected HttpRequest getRequest() {
174 return request;
175 }
176
177 public abstract T getResult();
178
179 @Override
180 public HandlerWithResult<T> setContext(HttpRequest request) {
181 this.request = request;
182 return this;
183 }
184 }
185
186 public abstract static class HandlerForGeneratedRequestWithResult<T> extends HandlerWithResult<T> {
187 @Override
188 protected GeneratedHttpRequest<?> getRequest() {
189 return (GeneratedHttpRequest<?>) super.getRequest();
190 }
191
192 @Override
193 public HandlerForGeneratedRequestWithResult<T> setContext(HttpRequest request) {
194 checkArgument(request instanceof GeneratedHttpRequest<?>, "note this handler requires a GeneratedHttpRequest");
195 super.setContext(request);
196 return this;
197 }
198 }
199
200 @Override
201 public ParseSax<T> setContext(HttpRequest request) {
202 handler.setContext(request);
203 this.request = request;
204 return this;
205 }
206 }