View Javadoc

1   /*
2    * $Id: RestfulClient.java 1529 2011-06-22 16:37:19Z hoheisel $
3    *
4    * Copyright 2008 Fraunhofer Gesellschaft, Munich, Germany,
5    * for its Fraunhofer Institute for Computer Architecture and Software Technology (FIRST), Berlin, Germany
6    * All rights reserved.
7    *
8    * See http://www.first.fraunhofer.de and http://www.gridworkflow.org/gwes for more details.
9    */
10  
11  package net.kwfgrid.gwes.restfulclient;
12  
13  import net.kwfgrid.gwes.exception.GWESException;
14  import org.apache.log4j.Logger;
15  import org.xmlpull.v1.XmlPullParser;
16  import org.xmlpull.v1.XmlPullParserFactory;
17  import org.xmlpull.v1.XmlPullParserException;
18  
19  import java.net.HttpURLConnection;
20  import java.net.URL;
21  import java.io.IOException;
22  import java.io.InputStream;
23  import java.io.OutputStream;
24  import java.io.ByteArrayInputStream;
25  import java.util.ArrayList;
26  import java.util.List;
27  import java.rmi.RemoteException;
28  
29  import net.kwfgrid.gwes.util.StringUtils;
30  
31  /**
32   * Class for generating REST requests.
33   * @author Andreas Hoheisel
34   *         (<a href="http://www.andreas-hoheisel.de">www.andreas-hoheisel.de</a>)
35   * @version $Id: RestfulClient.java 1529 2011-06-22 16:37:19Z hoheisel $
36   */
37  public class RestfulClient {
38  
39      final static Logger logger = Logger.getLogger(RestfulClient.class);
40      private static final String ELEMENT_RETURN = "ns:return";
41  
42      /**
43       * HTTP RESTful method.
44       */
45      public static enum Method {
46          GET,
47          POST,
48          HEAD,
49          OPTIONS,
50          PUT,
51          DELETE,
52          TRACE
53      }
54  
55      private static final String GET = "GET";
56      private static final String POST = "POST";
57      private static final String HEAD = "HEAD";
58      private static final String OPTIONS = "OPTIONS";
59      private static final String PUT = "PUT";
60      private static final String DELETE = "DELETE";
61      private static final String TRACE = "TRACE";
62  
63      /**
64       * Invoke a RESTful GET request and return the response as list of strings.
65       * @param url The target URL
66       * @return A list of Strings containing the TEXT values of the XML response message.
67       * @throws RemoteException If a parsing or IO exception of the response message occurrs.
68       */
69      public String[] httpGET(String url) throws RemoteException {
70          try {
71              List response = request(new URL(url), Method.GET, null);
72              return (String[]) response.toArray(new String[0]);
73          } catch (IOException e) {
74              throw new RemoteException("exception during HTTP GET "+url+": " + e, e);
75          } catch (XmlPullParserException e) {
76              throw new RemoteException("exception during HTTP GET "+url+": " + e, e);
77          }
78      }
79  
80      /**
81       * Invoke a RESTful GET request and return the response as list of strings.
82       * @param url The target URL
83       * @return A list of Strings containing the TEXT values of the XML response message.
84       * @throws RemoteException If a parsing or IO exception of the response message occurrs.
85       */
86      public String[][] httpGETArray(String url) throws RemoteException {
87          try {
88              List<List<String>> response = requestArray(new URL(url), Method.GET, null);
89              return StringUtils.convertListListToStringArrayArray(response);
90          } catch (IOException e) {
91              throw new RemoteException("exception during HTTP GET "+url+": " + e, e);
92          } catch (XmlPullParserException e) {
93              throw new RemoteException("exception during HTTP GET "+url+": " + e, e);
94          }
95      }
96  
97      /**
98       * Invoke a RESTful PUT request and return the response as list of strings.
99       * @param url The target URL
100      * @return A list of Strings containing the TEXT values of the XML response message.
101      * @throws RemoteException If a parsing or IO exception of the response message occurrs.
102      */
103     public String[] httpPUT(String url, String payload) throws RemoteException {
104         try {
105             InputStream in = null;
106             if (payload != null) {
107                 in = new ByteArrayInputStream(payload.getBytes("UTF-8"));
108             }
109             List response = request(new URL(url), Method.PUT, in);
110             return (String[]) response.toArray(new String[0]);
111         } catch (IOException e) {
112             throw new RemoteException("exception during HTTP PUT "+url+": " + e, e);
113         } catch (XmlPullParserException e) {
114             throw new RemoteException("exception during HTTP PUT "+url+": " + e, e);
115         }
116     }
117 
118     /**
119      * Invoke a RESTful POST request and return the response as list of strings.
120      * @param url The target URL
121      * @return A list of Strings containing the TEXT values of the XML response message.
122      * @throws RemoteException If a parsing or IO exception of the response message occurs.
123      */
124     public String[] httpPOST(String url, String payload) throws RemoteException {
125         try {
126             InputStream in = null;
127             if (payload != null) {
128                 in = new ByteArrayInputStream(payload.getBytes("UTF-8"));
129             }
130             List response = request(new URL(url), Method.POST, in);
131             return (String[]) response.toArray(new String[0]);
132         } catch (IOException e) {
133             throw new RemoteException("exception during HTTP POST "+url+": " + e, e);
134         } catch (XmlPullParserException e) {
135             throw new RemoteException("exception during HTTP POST "+url+": " + e, e);
136         }
137     }
138 
139     /**
140      * Invoke a RESTful DELETE request and return the response as list of strings.
141      * @param url The target URL
142      * @return A list of Strings containing the TEXT values of the XML response message.
143      * @throws RemoteException If a parsing or IO exception of the response message occurs.
144      */
145     public String[] httpDELETE(String url) throws RemoteException {
146         try {
147             List response = request(new URL(url), Method.DELETE, null);
148             return (String[]) response.toArray(new String[0]);
149         } catch (IOException e) {
150             throw new RemoteException("exception during HTTP DELETE "+url+": " + e, e);
151         } catch (XmlPullParserException e) {
152             throw new RemoteException("exception during HTTP DELETE "+url+": " + e, e);
153         }
154     }
155 
156     /**
157      * Invoke a restful request and return the response as list of strings.
158      * @param url The target URL.
159      * @param method The method (GET, PUT, ...).
160      * @param payload The payload body used with POST and PUT
161      * @return A list of Strings containing the TEXT values of the XML response message.
162      * @throws IOException
163      * @throws XmlPullParserException
164      */
165     public static List<String> request(URL url, Method method, InputStream payload) throws XmlPullParserException, IOException {
166 
167         if (logger.isDebugEnabled()) {
168             logger.debug("trying to connect to "+url.toString()+" using method "+getMethodAsString(method)+"...");
169         }
170 
171         HttpURLConnection connection = (HttpURLConnection)url.openConnection();
172         connection.setRequestMethod(getMethodAsString(method));
173 
174 //        // write auth header
175 //        BASE64Encoder encoder = new BASE64Encoder();
176 //        String encodedCredential = encoder.encode( (username + ":" + password).getBytes() );
177 //        connection.setRequestProperty("Authorization", "BASIC " + encodedCredential);
178 
179         writeBodyToConnection(payload, connection);
180 
181         ArrayList<String> list = new ArrayList<String>();
182         try {
183             // do request
184             connection.connect();
185             parseXmlTextToStringList(connection.getInputStream(), list);
186         } catch (IOException e) {
187             throw new RemoteException("Error message returned by server: \n"+ StringUtils.convertStreamToString(connection.getErrorStream()),e);
188         }
189 
190         connection.disconnect();
191 
192 //        // look at headers
193 //        // the 0th header has a null key, and the value is the response line ("HTTP/1.1 200 OK" or whatever)
194 //        String header = null;
195 //        String headerValue = null;
196 //        int index = 0;
197 //        while ((headerValue = connection.getHeaderField(index)) != null)
198 //        {
199 //            header = connection.getHeaderFieldKey(index);
200 //
201 //            if (header == null)
202 //                System.out.println(headerValue);
203 //            else
204 //                System.out.println(header + ": " + headerValue);
205 //
206 //            index++;
207 //        }
208 //        System.out.println("");
209 
210         return list;
211     }
212 
213     /**
214      * Invoke a restful request and return the response as list of list of strings.
215      * @param url The target URL.
216      * @param method The method (GET, PUT, ...).
217      * @param payload The payload body used with POST and PUT
218      * @return A list of list of Strings containing the TEXT values of the XML response message array.
219      * @throws IOException
220      * @throws XmlPullParserException
221      */
222     public static List<List<String>> requestArray(URL url, Method method, InputStream payload) throws XmlPullParserException, IOException {
223 
224         if (logger.isDebugEnabled()) {
225             logger.debug("trying to connect to "+url.toString()+" using method "+getMethodAsString(method)+"...");
226         }
227 
228         HttpURLConnection connection = (HttpURLConnection)url.openConnection();
229         connection.setRequestMethod(getMethodAsString(method));
230         writeBodyToConnection(payload, connection);
231 
232         List<List<String>> listList = new ArrayList<List<String>>();
233         try {
234             // do request
235             connection.connect();
236             parseXmlTextToStringListList(connection.getInputStream(), listList);
237         } catch (IOException e) {
238             throw new RemoteException("Error message returned by server: \n"+ StringUtils.convertStreamToString(connection.getErrorStream()),e);
239         }
240 
241         connection.disconnect();
242         return listList;
243     }
244 
245     private static void writeBodyToConnection(InputStream payload, HttpURLConnection connection) throws IOException {
246         // write body if we're doing POST or PUT
247         int read = 0;
248         if (payload != null) {
249             byte buffer[] = new byte[8192];
250             connection.setDoOutput(true);
251             OutputStream output = connection.getOutputStream();
252             while ((read = payload.read(buffer)) != -1) {
253                 output.write(buffer, 0, read);
254             }
255         }
256     }
257 
258     private static void parseXmlTextToStringList(InputStream in, List<String> list) throws XmlPullParserException, IOException {
259         XmlPullParser parser = getParser(in);
260         int event = parser.next();
261         int lastEvent;
262         while (event != XmlPullParser.END_DOCUMENT) {
263             boolean startTag = false;
264             if (event == XmlPullParser.TEXT) {
265                 list.add(parser.getText());
266             }
267             lastEvent = event;
268             event = parser.next();
269             // detect empty element
270             if (lastEvent == XmlPullParser.START_TAG && event == XmlPullParser.END_TAG) {
271                 list.add("");
272             }
273         }
274     }
275 
276     /**
277      * parse XML and put text contents into nested list of list of strings.
278      * Example input XML:
279      * <pre>
280      * &lt;ns:getModificationsForUpdateResponse xmlns:ns=&quot;http://gwes.kwfgrid.net&quot;
281      *   &lt;ns:return&gt;
282      *     &lt;ns:array&gt;27&lt;/ns:array&gt;
283      *     &lt;ns:array&gt;MODIFICATION&lt;/ns:array&gt;
284      *   &lt;/ns:return&gt;
285      *   &lt;ns:return&gt;
286      *     &lt;ns:array&gt;28&lt;/ns:array&gt;
287      *     &lt;ns:array&gt;MODIFICATION&lt;/ns:array&gt;
288      *   &lt;/ns:return&gt;
289      * &lt;/ns:getModificationsForUpdateResponse&gt;
290      * </pre>
291      * @param in
292      * @param listList
293      * @throws XmlPullParserException
294      * @throws IOException
295      */
296     private static void parseXmlTextToStringListList(InputStream in, List<List<String>> listList) throws XmlPullParserException, IOException {
297         //        <ns:getModificationsForUpdateResponse xmlns:ns="http://gwes.kwfgrid.net"
298         //           <ns:return>
299         //             <ns:array>27</ns:array>
300         //             <ns:array>MODIFICATION</ns:array>
301         //           </ns:return>
302         //           <ns:return>
303         //             <ns:array>28</ns:array>
304         //             <ns:array>MODIFICATION</ns:array>
305         //           </ns:return>
306         //        </ns:getModificationsForUpdateResponse>
307 
308         List<String> list = null;
309 
310         XmlPullParser parser = getParser(in);
311         int event = parser.next();
312         int lastEvent = -1;
313         while (event != XmlPullParser.END_DOCUMENT) {
314             // <ns:return>
315             if (event == XmlPullParser.START_TAG && parser.getName().equals(ELEMENT_RETURN)) {
316                 list = new ArrayList<String>();
317             }
318             else if (event == XmlPullParser.TEXT && list != null) {
319                 list.add(parser.getText());
320             }
321             else if (event == XmlPullParser.END_TAG && list != null) {
322                 // detect empty element
323                 if (lastEvent == XmlPullParser.START_TAG) {
324                     list.add("");
325                 }
326                 // </ns:return>
327                 if (parser.getName().equals(ELEMENT_RETURN)) {
328                     listList.add(list);
329                     list = null;
330                 }
331             }
332             lastEvent = event;
333             event = parser.next();
334         }
335     }
336 
337     /**
338      * Get HTTP REST method as String.
339      * @param method The REST method.
340      * @return A String representing the REST method, e.g., "GET", "PUT", ....
341      */
342     public static String getMethodAsString(Method method) {
343         switch (method) {
344             case DELETE:
345                 return DELETE;
346             case GET:
347                 return GET;
348             case HEAD:
349                 return HEAD;
350             case OPTIONS:
351                 return OPTIONS;
352             case POST:
353                 return POST;
354             case PUT:
355                 return PUT;
356             case TRACE:
357                 return TRACE;
358         }
359         return null;
360     }
361 
362     private static XmlPullParser getParser(InputStream inputStream) throws XmlPullParserException {
363         XmlPullParserFactory factory = XmlPullParserFactory.newInstance();
364         XmlPullParser parser = factory.newPullParser();
365         parser.setInput(inputStream,null);
366         return parser;
367     }
368 
369 }