/* * Copyright 2008-2009 LinkedIn, Inc * * Licensed under the Apache License, Version 2.0 (the "License"); you may not * use this file except in compliance with the License. You may obtain a copy of * the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the * License for the specific language governing permissions and limitations under * the License. */ package voldemort.store.http; import java.io.ByteArrayOutputStream; import java.io.DataInputStream; import java.io.DataOutputStream; import java.io.IOException; import java.net.HttpURLConnection; import java.util.List; import java.util.Map; import org.apache.commons.io.IOUtils; import org.apache.http.HttpResponse; import org.apache.http.client.HttpClient; import org.apache.http.client.methods.HttpPost; import org.apache.http.entity.ByteArrayEntity; import voldemort.VoldemortException; import voldemort.client.protocol.RequestFormat; import voldemort.server.RequestRoutingType; import voldemort.store.AbstractStore; import voldemort.store.StoreUtils; import voldemort.store.UnreachableStoreException; import voldemort.utils.ByteArray; import voldemort.utils.VoldemortIOUtils; import voldemort.versioning.VectorClock; import voldemort.versioning.Version; import voldemort.versioning.Versioned; /** * A remote store client that transmits operations via HTTP and interacts with * the VoldemortHttpServer. * */ public class HttpStore extends AbstractStore<ByteArray, byte[], byte[]> { private final HttpClient httpClient; private final RequestFormat requestFormat; private final RequestRoutingType reroute; private final String storeUrl; public HttpStore(String storeName, String host, int port, HttpClient client, RequestFormat format, boolean reroute) { super(storeName); this.httpClient = client; this.requestFormat = format; this.reroute = RequestRoutingType.getRequestRoutingType(reroute, false); this.storeUrl = "http://" + host + ":" + port + "/stores"; } @Override public boolean delete(ByteArray key, Version version) throws VoldemortException { StoreUtils.assertValidKey(key); DataInputStream input = null; try { HttpPost method = new HttpPost(this.storeUrl); ByteArrayOutputStream outputBytes = new ByteArrayOutputStream(); requestFormat.writeDeleteRequest(new DataOutputStream(outputBytes), getName(), key, (VectorClock) version, reroute); input = executeRequest(method, outputBytes); return requestFormat.readDeleteResponse(input); } catch(IOException e) { throw new UnreachableStoreException("Could not connect to " + storeUrl + " for " + getName(), e); } finally { IOUtils.closeQuietly(input); } } @Override public List<Versioned<byte[]>> get(ByteArray key, byte[] transforms) throws VoldemortException { StoreUtils.assertValidKey(key); DataInputStream input = null; try { HttpPost method = new HttpPost(this.storeUrl); ByteArrayOutputStream outputBytes = new ByteArrayOutputStream(); requestFormat.writeGetRequest(new DataOutputStream(outputBytes), getName(), key, transforms, reroute); input = executeRequest(method, outputBytes); return requestFormat.readGetResponse(input); } catch(IOException e) { throw new UnreachableStoreException("Could not connect to " + storeUrl + " for " + getName(), e); } finally { IOUtils.closeQuietly(input); } } @Override public Map<ByteArray, List<Versioned<byte[]>>> getAll(Iterable<ByteArray> keys, Map<ByteArray, byte[]> transforms) throws VoldemortException { StoreUtils.assertValidKeys(keys); DataInputStream input = null; try { HttpPost method = new HttpPost(this.storeUrl); ByteArrayOutputStream outputBytes = new ByteArrayOutputStream(); requestFormat.writeGetAllRequest(new DataOutputStream(outputBytes), getName(), keys, transforms, reroute); input = executeRequest(method, outputBytes); return requestFormat.readGetAllResponse(input); } catch(IOException e) { throw new UnreachableStoreException("Could not connect to " + storeUrl + " for " + getName(), e); } finally { IOUtils.closeQuietly(input); } } @Override public void put(ByteArray key, Versioned<byte[]> versioned, byte[] transforms) throws VoldemortException { StoreUtils.assertValidKey(key); DataInputStream input = null; try { HttpPost method = new HttpPost(this.storeUrl); ByteArrayOutputStream outputBytes = new ByteArrayOutputStream(); requestFormat.writePutRequest(new DataOutputStream(outputBytes), getName(), key, versioned.getValue(), transforms, (VectorClock) versioned.getVersion(), reroute); input = executeRequest(method, outputBytes); requestFormat.readPutResponse(input); } catch(IOException e) { throw new UnreachableStoreException("Could not connect to " + storeUrl + " for " + getName(), e); } finally { IOUtils.closeQuietly(input); } } private DataInputStream executeRequest(HttpPost method, ByteArrayOutputStream output) { HttpResponse response = null; try { method.setEntity(new ByteArrayEntity(output.toByteArray())); response = httpClient.execute(method); int statusCode = response.getStatusLine().getStatusCode(); if(statusCode != HttpURLConnection.HTTP_OK) { String message = response.getStatusLine().getReasonPhrase(); VoldemortIOUtils.closeQuietly(response); throw new UnreachableStoreException("HTTP request to store " + getName() + " returned status code " + statusCode + " " + message); } return new DataInputStream(response.getEntity().getContent()); } catch(IOException e) { VoldemortIOUtils.closeQuietly(response); throw new UnreachableStoreException("Could not connect to " + storeUrl + " for " + getName(), e); } } @Override public List<Version> getVersions(ByteArray key) { StoreUtils.assertValidKey(key); DataInputStream input = null; try { HttpPost method = new HttpPost(this.storeUrl); ByteArrayOutputStream outputBytes = new ByteArrayOutputStream(); requestFormat.writeGetVersionRequest(new DataOutputStream(outputBytes), getName(), key, reroute); input = executeRequest(method, outputBytes); return requestFormat.readGetVersionResponse(input); } catch(IOException e) { throw new UnreachableStoreException("Could not connect to " + storeUrl + " for " + getName(), e); } finally { IOUtils.closeQuietly(input); } } }