001/* 002 * ModeShape (http://www.modeshape.org) 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * http://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016package org.modeshape.jdbc.rest; 017 018import java.io.ByteArrayOutputStream; 019import java.io.IOException; 020import java.io.InputStream; 021import java.net.HttpURLConnection; 022import java.net.MalformedURLException; 023import java.net.URI; 024import java.net.URISyntaxException; 025import java.net.URL; 026import org.apache.http.HttpEntity; 027import org.apache.http.HttpHost; 028import org.apache.http.HttpResponse; 029import org.apache.http.auth.AuthScope; 030import org.apache.http.auth.UsernamePasswordCredentials; 031import org.apache.http.client.HttpClient; 032import org.apache.http.client.methods.HttpEntityEnclosingRequestBase; 033import org.apache.http.client.methods.HttpGet; 034import org.apache.http.client.methods.HttpPost; 035import org.apache.http.client.methods.HttpRequestBase; 036import org.apache.http.client.protocol.HttpClientContext; 037import org.apache.http.client.utils.URIBuilder; 038import org.apache.http.entity.BufferedHttpEntity; 039import org.apache.http.entity.InputStreamEntity; 040import org.apache.http.impl.client.BasicCredentialsProvider; 041import org.apache.http.impl.client.HttpClientBuilder; 042import org.apache.http.util.EntityUtils; 043import org.codehaus.jettison.json.JSONException; 044import org.codehaus.jettison.json.JSONObject; 045import org.modeshape.common.text.UrlEncoder; 046import org.modeshape.common.util.CheckArg; 047import org.modeshape.common.util.StringUtil; 048 049/** 050 * A simple client which can be used to make REST calls to a server. 051 * 052 * @author Horia Chiorean (hchiorea@redhat.com) 053 */ 054@SuppressWarnings( "deprecation" ) 055public final class JSONRestClient { 056 057 private static final UrlEncoder URL_ENCODER = new UrlEncoder().setSlashEncoded(false); 058 protected final HttpClient httpClient; 059 private final HttpClientContext httpContext; 060 private final HttpHost host; 061 private final String url; 062 private final String baseUrl; 063 064 protected JSONRestClient( String url, 065 String username, 066 String password ) { 067 CheckArg.isNotNull(url, "url"); 068 try { 069 this.url = url; 070 URL connectionUrl = new URL(url); 071 this.host = new HttpHost(connectionUrl.getHost(), connectionUrl.getPort(), connectionUrl.getProtocol()); 072 String urlPath = connectionUrl.getPath(); 073 if (urlPath.length() > 0) { 074 String[] segments = urlPath.split("\\/"); 075 this.baseUrl = this.host.toURI() + "/" + (segments[0].length() > 0 ? segments[0] : segments[1]); 076 } else { 077 this.baseUrl = this.host.toURI(); 078 } 079 this.httpClient = HttpClientBuilder.create().build(); 080 this.httpContext = HttpClientContext.create(); 081 if (!StringUtil.isBlank(username)) { 082 BasicCredentialsProvider credentialsProvider = new BasicCredentialsProvider(); 083 credentialsProvider.setCredentials(new AuthScope(host()), 084 new UsernamePasswordCredentials(username, password)); 085 httpContext.setCredentialsProvider(credentialsProvider); 086 } 087 } catch (MalformedURLException e) { 088 throw new IllegalArgumentException("Invalid URL string: " + url, e); 089 } 090 } 091 092 protected String url() { 093 return url; 094 } 095 096 protected HttpHost host() { 097 return host; 098 } 099 100 protected Response doGet() { 101 return new Response(newJSONRequest(HttpGet.class, null, null, null)); 102 } 103 104 protected Response doGet( String url ) { 105 return new Response(newJSONRequest(HttpGet.class, null, null, url)); 106 } 107 108 protected Response postStream( InputStream is, 109 String url, 110 String requestContentType ) { 111 HttpPost post = newJSONRequest(HttpPost.class, is, requestContentType, url); 112 return new Response(post); 113 } 114 115 protected Response postStreamTextPlain( InputStream is, 116 String url, 117 String requestContentType ) { 118 HttpPost post = newRequest(HttpPost.class, is, requestContentType, MediaType.TEXT_PLAIN, url); 119 return new Response(post); 120 } 121 122 private <T extends HttpRequestBase> T newJSONRequest( Class<T> clazz, 123 InputStream inputStream, 124 String contentType, 125 String url ) { 126 return newRequest(clazz, inputStream, contentType, MediaType.APPLICATION_JSON, url); 127 } 128 129 private <T extends HttpRequestBase> T newRequest( Class<T> clazz, 130 InputStream inputStream, 131 String contentType, 132 String accepts, 133 String url ) { 134 assert accepts != null; 135 try { 136 if (url == null) { 137 url = baseUrl; 138 } else if (!url.startsWith(host.getSchemeName())) { 139 url = appendToBaseURL(url); 140 } 141 URIBuilder uriBuilder; 142 try { 143 uriBuilder = new URIBuilder(url); 144 } catch (URISyntaxException e) { 145 uriBuilder = new URIBuilder(URL_ENCODER.encode(url)); 146 } 147 148 T result = clazz.getConstructor(URI.class).newInstance(uriBuilder.build()); 149 result.setHeader("Accept", accepts); 150 if (contentType != null) { 151 result.setHeader("Content-Type", contentType); 152 } 153 if (inputStream != null) { 154 assert result instanceof HttpEntityEnclosingRequestBase; 155 InputStreamEntity inputStreamEntity = new InputStreamEntity(inputStream, inputStream.available()); 156 ((HttpEntityEnclosingRequestBase)result).setEntity(new BufferedHttpEntity(inputStreamEntity)); 157 } 158 159 return result; 160 } catch (Exception e) { 161 throw new RuntimeException(e); 162 } 163 } 164 165 protected static String append( String url, 166 String... segments ) { 167 for (String segment : segments) { 168 if (url.endsWith(segment)) { 169 continue; 170 } 171 if (!url.endsWith("/")) { 172 url = url + "/"; 173 } 174 if (segment.startsWith("/")) { 175 segment = segment.substring(1); 176 } 177 url += segment; 178 } 179 return url; 180 } 181 182 protected String appendToBaseURL( String... segments ) { 183 return append(baseUrl, segments); 184 } 185 186 protected String appendToURL( String... segments ) { 187 return append(url, segments); 188 } 189 190 protected interface MediaType { 191 String APPLICATION_JSON = "application/json"; 192 String TEXT_PLAIN = "text/plain;"; 193 } 194 195 protected class Response { 196 197 private final HttpResponse response; 198 private byte[] content; 199 private String contentString; 200 private JSONObject contentJSON; 201 202 protected Response( HttpRequestBase request ) { 203 try { 204 response = httpClient.execute(host(), request, httpContext); 205 HttpEntity entity = response.getEntity(); 206 if (entity != null) { 207 ByteArrayOutputStream baous = new ByteArrayOutputStream(); 208 entity.writeTo(baous); 209 EntityUtils.consumeQuietly(entity); 210 content = baous.toByteArray(); 211 } else { 212 content = new byte[0]; 213 } 214 } catch (IOException e) { 215 throw new RuntimeException(e); 216 } finally { 217 request.releaseConnection(); 218 } 219 } 220 221 public boolean isOK() { 222 return hasCode(HttpURLConnection.HTTP_OK); 223 } 224 225 private int getStatusCode() { 226 return response.getStatusLine().getStatusCode(); 227 } 228 229 private boolean hasCode( int statusCode ) { 230 return getStatusCode() == statusCode; 231 } 232 233 public JSONObject json() { 234 try { 235 if (contentJSON == null) { 236 contentJSON = new JSONObject(asString()); 237 } 238 return contentJSON; 239 } catch (JSONException e) { 240 throw new RuntimeException(e); 241 } 242 } 243 244 public String asString() { 245 if (contentString == null) { 246 contentString = new String(content); 247 } 248 return contentString; 249 } 250 251 @Override 252 public String toString() { 253 return asString(); 254 } 255 } 256}