Java tutorial
/* * Copyright 2013 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.coordinator; import static org.jboss.netty.handler.codec.http.HttpHeaders.Names.CONTENT_LENGTH; import static org.jboss.netty.handler.codec.http.HttpHeaders.Names.CONTENT_LOCATION; import static org.jboss.netty.handler.codec.http.HttpHeaders.Names.CONTENT_TRANSFER_ENCODING; import static org.jboss.netty.handler.codec.http.HttpHeaders.Names.CONTENT_TYPE; import static org.jboss.netty.handler.codec.http.HttpHeaders.Names.ETAG; import static org.jboss.netty.handler.codec.http.HttpResponseStatus.BAD_REQUEST; import static org.jboss.netty.handler.codec.http.HttpResponseStatus.INTERNAL_SERVER_ERROR; import static org.jboss.netty.handler.codec.http.HttpResponseStatus.NOT_FOUND; import static org.jboss.netty.handler.codec.http.HttpResponseStatus.OK; import static org.jboss.netty.handler.codec.http.HttpResponseStatus.REQUEST_TIMEOUT; import static org.jboss.netty.handler.codec.http.HttpVersion.HTTP_1_1; import java.io.IOException; import java.util.List; import java.util.Map; import java.util.Map.Entry; import javax.mail.MessagingException; import javax.mail.internet.MimeBodyPart; import javax.mail.internet.MimeMultipart; import org.apache.commons.codec.binary.Base64; import org.apache.commons.io.output.ByteArrayOutputStream; import org.apache.log4j.Logger; import org.codehaus.jackson.JsonGenerationException; import org.codehaus.jackson.map.JsonMappingException; import org.codehaus.jackson.map.ObjectMapper; import org.jboss.netty.buffer.ChannelBuffer; import org.jboss.netty.buffer.ChannelBuffers; import org.jboss.netty.channel.MessageEvent; import org.jboss.netty.handler.codec.http.DefaultHttpResponse; import org.jboss.netty.handler.codec.http.HttpResponse; import voldemort.VoldemortException; import voldemort.store.CompositeVoldemortRequest; import voldemort.store.InsufficientOperationalNodesException; import voldemort.store.StoreTimeoutException; import voldemort.store.stats.StoreStats; import voldemort.store.stats.Tracked; import voldemort.utils.ByteArray; import voldemort.utils.Time; import voldemort.versioning.VectorClock; import voldemort.versioning.Versioned; /** * A Runnable class that uses the specified Fat client to perform a Voldemort * GET operation. This is invoked by a FatClientWrapper thread to satisfy a * corresponding REST GET request. * */ public class HttpGetAllRequestExecutor implements Runnable { private MessageEvent getRequestMessageEvent; DynamicTimeoutStoreClient<ByteArray, byte[]> storeClient; private final Logger logger = Logger.getLogger(HttpGetRequestExecutor.class); private final CompositeVoldemortRequest<ByteArray, byte[]> getAllRequestObject; private final String storeName; private final long startTimestampInNs; private final StoreStats coordinatorPerfStats; /** * * @param getAllRequestObject The request object containing key and timeout * values * @param requestEvent Reference to the MessageEvent for the response / * error * @param storeClient Reference to the fat client for performing this Get * operation * @param storeName Name of the store intended to be included in the * response (content-location) * @param coordinatorPerfStats Stats object used to measure the turnaround * time * @param startTimestampInNs start timestamp of the request */ public HttpGetAllRequestExecutor(CompositeVoldemortRequest<ByteArray, byte[]> getAllRequestObject, MessageEvent requestMessageEvent, DynamicTimeoutStoreClient<ByteArray, byte[]> storeClient, String storeName, long startTimestampInNs, StoreStats coordinatorPerfStats) { this.getRequestMessageEvent = requestMessageEvent; this.storeClient = storeClient; this.getAllRequestObject = getAllRequestObject; this.storeName = storeName; this.startTimestampInNs = startTimestampInNs; this.coordinatorPerfStats = coordinatorPerfStats; } public void writeResponse(Map<ByteArray, List<Versioned<byte[]>>> versionedResponses) throws Exception { // multiPartKeys is the outer multipart MimeMultipart multiPartKeys = new MimeMultipart(); ByteArrayOutputStream keysOutputStream = new ByteArrayOutputStream(); for (Entry<ByteArray, List<Versioned<byte[]>>> entry : versionedResponses.entrySet()) { ByteArray key = entry.getKey(); String contentLocationKey = "/" + this.storeName + "/" + new String(Base64.encodeBase64(key.get())); // Create the individual body part - for each key requested MimeBodyPart keyBody = new MimeBodyPart(); try { // Add the right headers keyBody.addHeader(CONTENT_TYPE, "multipart/binary"); keyBody.addHeader(CONTENT_TRANSFER_ENCODING, "binary"); keyBody.addHeader(CONTENT_LOCATION, contentLocationKey); } catch (MessagingException me) { logger.error("Exception while constructing key body headers", me); keysOutputStream.close(); throw me; } // multiPartValues is the inner multipart MimeMultipart multiPartValues = new MimeMultipart(); for (Versioned<byte[]> versionedValue : entry.getValue()) { byte[] responseValue = versionedValue.getValue(); VectorClock vectorClock = (VectorClock) versionedValue.getVersion(); String serializedVC = CoordinatorUtils.getSerializedVectorClock(vectorClock); // Create the individual body part - for each versioned value of // a key MimeBodyPart valueBody = new MimeBodyPart(); try { // Add the right headers valueBody.addHeader(CONTENT_TYPE, "application/octet-stream"); valueBody.addHeader(CONTENT_TRANSFER_ENCODING, "binary"); valueBody.addHeader(VoldemortHttpRequestHandler.X_VOLD_VECTOR_CLOCK, serializedVC); valueBody.setContent(responseValue, "application/octet-stream"); multiPartValues.addBodyPart(valueBody); } catch (MessagingException me) { logger.error("Exception while constructing value body part", me); keysOutputStream.close(); throw me; } } try { // Add the inner multipart as the content of the outer body part keyBody.setContent(multiPartValues); multiPartKeys.addBodyPart(keyBody); } catch (MessagingException me) { logger.error("Exception while constructing key body part", me); keysOutputStream.close(); throw me; } } try { multiPartKeys.writeTo(keysOutputStream); } catch (Exception e) { logger.error("Exception while writing mutipart to output stream", e); throw e; } ChannelBuffer responseContent = ChannelBuffers.dynamicBuffer(); responseContent.writeBytes(keysOutputStream.toByteArray()); // Create the Response object HttpResponse response = new DefaultHttpResponse(HTTP_1_1, OK); // Set the right headers response.setHeader(CONTENT_TYPE, "multipart/binary"); response.setHeader(CONTENT_TRANSFER_ENCODING, "binary"); // Copy the data into the payload response.setContent(responseContent); response.setHeader(CONTENT_LENGTH, response.getContent().readableBytes()); // Update the stats if (this.coordinatorPerfStats != null) { long durationInNs = System.nanoTime() - startTimestampInNs; this.coordinatorPerfStats.recordTime(Tracked.GET_ALL, durationInNs); } // Write the response to the Netty Channel this.getRequestMessageEvent.getChannel().write(response); keysOutputStream.close(); } public void writeResponseOld(Map<ByteArray, Versioned<byte[]>> responseVersioned) { // Multipart response MimeMultipart mp = new MimeMultipart(); ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); try { for (Entry<ByteArray, Versioned<byte[]>> entry : responseVersioned.entrySet()) { Versioned<byte[]> value = entry.getValue(); ByteArray keyByteArray = entry.getKey(); String base64Key = new String(Base64.encodeBase64(keyByteArray.get())); String contentLocationKey = "/" + this.storeName + "/" + base64Key; byte[] responseValue = value.getValue(); VectorClock vc = (VectorClock) value.getVersion(); VectorClockWrapper vcWrapper = new VectorClockWrapper(vc); ObjectMapper mapper = new ObjectMapper(); String eTag = ""; try { eTag = mapper.writeValueAsString(vcWrapper); } catch (JsonGenerationException e) { e.printStackTrace(); } catch (JsonMappingException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } if (logger.isDebugEnabled()) { logger.debug("ETAG : " + eTag); } // Create the individual body part MimeBodyPart body = new MimeBodyPart(); body.addHeader(CONTENT_TYPE, "application/octet-stream"); body.addHeader(CONTENT_LOCATION, contentLocationKey); body.addHeader(CONTENT_TRANSFER_ENCODING, "binary"); body.addHeader(CONTENT_LENGTH, "" + responseValue.length); body.addHeader(ETAG, eTag); body.setContent(responseValue, "application/octet-stream"); mp.addBodyPart(body); } // At this point we have a complete multi-part response mp.writeTo(outputStream); } catch (MessagingException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } ChannelBuffer responseContent = ChannelBuffers.dynamicBuffer(); responseContent.writeBytes(outputStream.toByteArray()); // 1. Create the Response object HttpResponse response = new DefaultHttpResponse(HTTP_1_1, OK); // 2. Set the right headers response.setHeader(CONTENT_TYPE, "multipart/binary"); response.setHeader(CONTENT_TRANSFER_ENCODING, "binary"); // 3. Copy the data into the payload response.setContent(responseContent); response.setHeader(CONTENT_LENGTH, response.getContent().readableBytes()); // Update the stats if (this.coordinatorPerfStats != null) { long durationInNs = System.nanoTime() - startTimestampInNs; this.coordinatorPerfStats.recordTime(Tracked.GET_ALL, durationInNs); } // Write the response to the Netty Channel this.getRequestMessageEvent.getChannel().write(response); } @Override public void run() { try { Map<ByteArray, List<Versioned<byte[]>>> versionedResponses = storeClient .getAllWithCustomTimeout(this.getAllRequestObject); if (versionedResponses == null || versionedResponses.entrySet().size() == 0) { RESTErrorHandler.handleError(NOT_FOUND, this.getRequestMessageEvent, "Requested Key does not exist"); } writeResponse(versionedResponses); } catch (IllegalArgumentException illegalArgsException) { String errorDescription = "GETALL Failed !!! Illegal Arguments : " + illegalArgsException.getMessage(); logger.error(errorDescription); RESTErrorHandler.handleError(BAD_REQUEST, this.getRequestMessageEvent, errorDescription); } catch (StoreTimeoutException timeoutException) { String errorDescription = "GET Request timed out: " + timeoutException.getMessage(); logger.error(errorDescription); RESTErrorHandler.handleError(REQUEST_TIMEOUT, this.getRequestMessageEvent, errorDescription); } catch (InsufficientOperationalNodesException exception) { long nowInNs = System.nanoTime(); if (nowInNs - startTimestampInNs > getAllRequestObject.getRoutingTimeoutInMs() * Time.NS_PER_MS) { String errorDescription = "GET Request timed out: " + exception.getMessage(); logger.error(errorDescription); RESTErrorHandler.handleError(REQUEST_TIMEOUT, this.getRequestMessageEvent, errorDescription); } else { String errorDescription = "Voldemort Exception: " + exception.getMessage(); RESTErrorHandler.handleError(INTERNAL_SERVER_ERROR, this.getRequestMessageEvent, errorDescription); } } catch (VoldemortException ve) { String errorDescription = "Voldemort Exception: " + ve.getMessage(); RESTErrorHandler.handleError(INTERNAL_SERVER_ERROR, this.getRequestMessageEvent, errorDescription); } catch (Exception e) { String errorDescription = "Exception: " + e.getMessage(); RESTErrorHandler.handleError(INTERNAL_SERVER_ERROR, this.getRequestMessageEvent, errorDescription); } } }