/* * Licensed to CRATE Technology GmbH ("Crate") under one or more contributor * license agreements. See the NOTICE file distributed with this work for * additional information regarding copyright ownership. Crate licenses * this file to you 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. * * However, if you have executed another commercial license agreement * with Crate these terms will supersede the license and you may use the * software solely pursuant to the terms of the relevant commercial agreement. */ package io.crate.blob; import io.crate.common.Hex; import org.apache.logging.log4j.Logger; import org.elasticsearch.client.Client; import org.elasticsearch.common.bytes.BytesArray; import org.elasticsearch.common.logging.Loggers; import org.jboss.netty.buffer.ChannelBuffer; import java.util.UUID; public class RemoteDigestBlob { private final String index; private Status status; public enum Status { FULL((byte) 0), PARTIAL((byte) 1), MISMATCH((byte) 2), EXISTS((byte) 3), FAILED((byte) 4); private final byte id; Status(byte id) { this.id = id; } /** * The internal representation of the status. */ public byte id() { return id; } public static Status fromId(byte id) { switch (id) { case 0: return FULL; case 1: return PARTIAL; case 2: return MISMATCH; case 3: return EXISTS; case 4: return FAILED; } throw new IllegalArgumentException("No status match for [" + id + "]"); } } private final static Logger logger = Loggers.getLogger(RemoteDigestBlob.class); private final String digest; private final Client client; private long size; private StartBlobResponse startResponse; private UUID transferId; RemoteDigestBlob(Client client, String index, String digest) { this.digest = digest; this.client = client; this.size = 0; this.index = index; } public Status status() { return status; } public boolean delete() { logger.trace("delete"); assert transferId == null : "transferId should be null"; DeleteBlobRequest request = new DeleteBlobRequest( index, Hex.decodeHex(digest) ); return client.execute(DeleteBlobAction.INSTANCE, request).actionGet().deleted; } private Status start(ChannelBuffer buffer, boolean last) { logger.trace("start blob upload"); assert transferId == null : "transferId should be null"; StartBlobRequest request = new StartBlobRequest( index, Hex.decodeHex(digest), new BytesArray(buffer.array()), last ); transferId = request.transferId(); size += buffer.readableBytes(); startResponse = client.execute(StartBlobAction.INSTANCE, request).actionGet(); status = startResponse.status(); return status; } private Status chunk(ChannelBuffer buffer, boolean last) { assert transferId != null : "transferId should not be null"; PutChunkRequest request = new PutChunkRequest( index, Hex.decodeHex(digest), transferId, new BytesArray(buffer.array()), size, last ); size += buffer.readableBytes(); PutChunkResponse putChunkResponse = client.execute(PutChunkAction.INSTANCE, request).actionGet(); return putChunkResponse.status(); } public Status addContent(ChannelBuffer buffer, boolean last) { if (startResponse == null) { // this is the first call to addContent return start(buffer, last); } else if (status == Status.EXISTS) { // client probably doesn't support 100-continue and is sending chunked requests // need to ignore the content. return status; } else if (status != Status.PARTIAL) { throw new IllegalStateException("Expected Status.PARTIAL for chunk but got: " + status); } else { return chunk(buffer, last); } } public long size() { return size; } }