/* * The Alluxio Open Foundation licenses this work under the Apache License, version 2.0 * (the "License"). You may not use this work except in compliance with the License, which is * available at www.apache.org/licenses/LICENSE-2.0 * * This software is distributed on an "AS IS" basis, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, * either express or implied, as more fully set forth in the License. * * See the NOTICE file distributed with this work for information regarding copyright ownership. */ package alluxio.worker.block; import alluxio.Constants; import alluxio.RestUtils; import alluxio.Sessions; import alluxio.StorageTierAssoc; import alluxio.WorkerStorageTierAssoc; import alluxio.web.WorkerWebServer; import alluxio.wire.LockBlockResult; import alluxio.worker.WorkerProcess; import alluxio.worker.block.io.BlockReader; import alluxio.worker.block.io.BlockWriter; import com.google.common.base.Preconditions; import com.qmino.miredot.annotations.ReturnType; import java.nio.ByteBuffer; import javax.annotation.concurrent.NotThreadSafe; import javax.servlet.ServletContext; import javax.ws.rs.Consumes; import javax.ws.rs.GET; import javax.ws.rs.POST; import javax.ws.rs.Path; import javax.ws.rs.Produces; import javax.ws.rs.QueryParam; import javax.ws.rs.core.Context; import javax.ws.rs.core.MediaType; import javax.ws.rs.core.Response; /** * This class is a REST handler for block worker requests. * * @deprecated since version 1.4 and will be removed in version 2.0 */ @NotThreadSafe @Path(BlockWorkerClientRestServiceHandler.SERVICE_PREFIX) @Deprecated public final class BlockWorkerClientRestServiceHandler { public static final String SERVICE_PREFIX = "worker/block"; public static final String SERVICE_NAME = "service_name"; public static final String SERVICE_VERSION = "service_version"; public static final String ACCESS_BLOCK = "access_block"; public static final String CACHE_BLOCK = "cache_block"; public static final String CANCEL_BLOCK = "cancel_block"; public static final String LOCK_BLOCK = "lock_block"; public static final String PROMOTE_BLOCK = "promote_block"; public static final String READ_BLOCK = "read_block"; public static final String REQUEST_BLOCK_LOCATION = "request_block_location"; public static final String REQUEST_SPACE = "request_space"; public static final String UNLOCK_BLOCK = "unlock_block"; public static final String WRITE_BLOCK = "write_block"; private final BlockWorker mBlockWorker; private final StorageTierAssoc mStorageTierAssoc; /** * @param context context for the servlet */ public BlockWorkerClientRestServiceHandler(@Context ServletContext context) { mBlockWorker = ((WorkerProcess) context.getAttribute(WorkerWebServer.ALLUXIO_WORKER_SERVLET_RESOURCE_KEY)) .getWorker(BlockWorker.class); mStorageTierAssoc = new WorkerStorageTierAssoc(); } /** * @summary get the service name * @return the response object */ @GET @Path(SERVICE_NAME) @Produces(MediaType.APPLICATION_JSON) @ReturnType("java.lang.String") public Response getServiceName() { return RestUtils.call(new RestUtils.RestCallable<String>() { @Override public String call() throws Exception { return Constants.BLOCK_WORKER_CLIENT_SERVICE_NAME; } }); } /** * @summary get the service version * @return the response object */ @GET @Path(SERVICE_VERSION) @Produces(MediaType.APPLICATION_JSON) @ReturnType("java.lang.Long") public Response getServiceVersion() { return RestUtils.call(new RestUtils.RestCallable<Long>() { @Override public Long call() throws Exception { return Constants.BLOCK_WORKER_CLIENT_SERVICE_VERSION; } }); } /** * @summary access a block * @param blockId the block id * @return the response object */ @POST @Path(ACCESS_BLOCK) @Produces(MediaType.APPLICATION_JSON) @ReturnType("java.lang.Void") public Response accessBlock(@QueryParam("blockId") final Long blockId) { return RestUtils.call(new RestUtils.RestCallable<Void>() { @Override public Void call() throws Exception { Preconditions.checkNotNull(blockId, "required 'blockId' parameter is missing"); mBlockWorker.accessBlock(Sessions.ACCESS_BLOCK_SESSION_ID, blockId); return null; } }); } /** * @summary cache a block * @param sessionId the session id * @param blockId the block id * @return the response object */ @POST @Path(CACHE_BLOCK) @Produces(MediaType.APPLICATION_JSON) @ReturnType("java.lang.Void") public Response cacheBlock(@QueryParam("sessionId") final Long sessionId, @QueryParam("blockId") final Long blockId) { return RestUtils.call(new RestUtils.RestCallable<Void>() { @Override public Void call() throws Exception { Preconditions.checkNotNull(blockId, "required 'blockId' parameter is missing"); Preconditions.checkNotNull(sessionId, "required 'sessionId' parameter is missing"); mBlockWorker.commitBlock(sessionId, blockId); return null; } }); } /** * @summary cancel a block * @param sessionId the session id * @param blockId the block id * @return the response object */ @POST @Path(CANCEL_BLOCK) @Produces(MediaType.APPLICATION_JSON) @ReturnType("java.lang.Void") public Response cancelBlock(@QueryParam("sessionId") final Long sessionId, @QueryParam("blockId") final Long blockId) { return RestUtils.call(new RestUtils.RestCallable<Void>() { @Override public Void call() throws Exception { Preconditions.checkNotNull(blockId, "required 'blockId' parameter is missing"); Preconditions.checkNotNull(sessionId, "required 'sessionId' parameter is missing"); mBlockWorker.abortBlock(sessionId, blockId); return null; } }); } /** * @summary lock a block * @param sessionId the session id * @param blockId the block id * @return the response object */ @POST @Path(LOCK_BLOCK) @Produces(MediaType.APPLICATION_JSON) @ReturnType("alluxio.wire.LockBlockResult") public Response lockBlock(@QueryParam("sessionId") final Long sessionId, @QueryParam("blockId") final Long blockId) { // NOTE: the logic here does not match the thrift lockBlock interface anymore since 1.5.0. return RestUtils.call(new RestUtils.RestCallable<LockBlockResult>() { @Override public LockBlockResult call() throws Exception { Preconditions.checkNotNull(blockId, "required 'blockId' parameter is missing"); Preconditions.checkNotNull(sessionId, "required 'sessionId' parameter is missing"); long lockId = mBlockWorker.lockBlock(sessionId, blockId); return new LockBlockResult().setLockId(lockId) .setBlockPath(mBlockWorker.readBlock(sessionId, blockId, lockId)).setLockBlockStatus( LockBlockResult.LockBlockStatus.ALLUXIO_BLOCK_LOCKED); } }); } /** * @summary promote a block * @param blockId the block id * @return the response object */ @POST @Path(PROMOTE_BLOCK) @Produces(MediaType.APPLICATION_JSON) @ReturnType("java.lang.Void") public Response promoteBlock(@QueryParam("blockId") final Long blockId) { return RestUtils.call(new RestUtils.RestCallable<Void>() { @Override public Void call() throws Exception { Preconditions.checkNotNull(blockId, "required 'blockId' parameter is missing"); mBlockWorker .moveBlock(Sessions.MIGRATE_DATA_SESSION_ID, blockId, mStorageTierAssoc.getAlias(0)); return null; } }); } /** * @summary read a block * @param sessionId the session id * @param blockId the block id * @param lockId the lock id * @param offset the offset to start the read at * @param length the number of bytes to read (the value -1 means read until EOF) * @return the response object */ @GET @Path(READ_BLOCK) @Produces(MediaType.APPLICATION_OCTET_STREAM) @ReturnType("java.util.List<java.lang.Byte>") public Response readBlock(@QueryParam("sessionId") final Long sessionId, @QueryParam("blockId") final Long blockId, @QueryParam("lockId") final Long lockId, @QueryParam("offset") final Long offset, @QueryParam("length") final Long length) { return RestUtils.call(new RestUtils.RestCallable<Object>() { @Override public Object call() throws Exception { Preconditions.checkNotNull(blockId, "required 'blockId' parameter is missing"); Preconditions.checkNotNull(sessionId, "required 'sessionId' parameter is missing"); Preconditions.checkNotNull(lockId, "required 'lockId' parameter is missing"); Preconditions.checkNotNull(offset, "required 'offset' parameter is missing"); Preconditions.checkNotNull(length, "required 'length' parameter is missing"); Preconditions.checkState(offset >= 0, "invalid offset: %s", offset); Preconditions.checkState(length >= -1, "invalid length (except for -1): %s", length); // TODO(jiri): Wrap this logic in a block worker function; requires refactoring. try (BlockReader reader = mBlockWorker.readBlockRemote(sessionId, blockId, lockId)) { long fileLength = reader.getLength(); Preconditions .checkArgument(offset <= fileLength, "offset %s is larger than file length %s", offset, fileLength); Preconditions.checkArgument(length == -1 || offset + length <= fileLength, "offset %s plus length %s is larger than file length %s", offset, length, fileLength); long readLength = (length == -1) ? fileLength - offset : length; ByteBuffer buffer = reader.read(offset, readLength); mBlockWorker.accessBlock(sessionId, blockId); if (buffer.hasArray()) { return buffer.array(); } // We need to copy the bytes because the buffer byte array cannot be accessed directly. byte[] bytes = new byte[(int) readLength]; buffer.get(bytes); return bytes; } } }); } /** * @summary request a block location * @param sessionId the session id * @param blockId the block id * @param initialBytes the initial number of bytes to allocate * @return the response object */ @POST @Path(REQUEST_BLOCK_LOCATION) @Produces(MediaType.APPLICATION_JSON) @ReturnType("java.lang.String") public Response requestBlockLocation(@QueryParam("sessionId") final Long sessionId, @QueryParam("blockId") final Long blockId, @QueryParam("initialBytes") final Long initialBytes) { return RestUtils.call(new RestUtils.RestCallable<String>() { @Override public String call() throws Exception { Preconditions.checkNotNull(blockId, "required 'blockId' parameter is missing"); Preconditions.checkNotNull(sessionId, "required 'sessionId' parameter is missing"); Preconditions.checkNotNull(initialBytes, "required 'initialBytes' parameter is missing"); return mBlockWorker .createBlock(sessionId, blockId, mStorageTierAssoc.getAlias(0), initialBytes); } }); } /** * @summary request additional space for a block * @param sessionId the session id * @param blockId the block id * @param requestBytes the additional number of bytes to allocate * @return the response object */ @POST @Path(REQUEST_SPACE) @Produces(MediaType.APPLICATION_JSON) @ReturnType("java.lang.Void") public Response requestSpace(@QueryParam("sessionId") final Long sessionId, @QueryParam("blockId") final Long blockId, @QueryParam("requestBytes") final Long requestBytes) { return RestUtils.call(new RestUtils.RestCallable<Void>() { @Override public Void call() throws Exception { Preconditions.checkNotNull(blockId, "required 'blockId' parameter is missing"); Preconditions.checkNotNull(sessionId, "required 'sessionId' parameter is missing"); Preconditions.checkNotNull(requestBytes, "required 'requestBytes' parameter is missing"); mBlockWorker.requestSpace(sessionId, blockId, requestBytes); return null; } }); } /** * @summary unlock a block * @param sessionId the session id * @param blockId the block id * @return the response object */ @POST @Path(UNLOCK_BLOCK) @Produces(MediaType.APPLICATION_JSON) @ReturnType("java.lang.Void") public Response unlockBlock(@QueryParam("sessionId") final Long sessionId, @QueryParam("blockId") final Long blockId) { return RestUtils.call(new RestUtils.RestCallable<Void>() { @Override public Void call() throws Exception { Preconditions.checkNotNull(blockId, "required 'blockId' parameter is missing"); Preconditions.checkNotNull(sessionId, "required 'sessionId' parameter is missing"); mBlockWorker.unlockBlock(sessionId, blockId); return null; } }); } /** * @summary write a block * @param sessionId the session id * @param blockId the block id * @param offset the offset to start the read at * @param length the number of bytes to read (the value -1 means read until EOF) * @param data the data to write * @return the response object */ @POST @Path(WRITE_BLOCK) @Produces(MediaType.APPLICATION_JSON) @Consumes(MediaType.APPLICATION_OCTET_STREAM) @ReturnType("java.lang.Void") public Response writeBlock(@QueryParam("sessionId") final Long sessionId, @QueryParam("blockId") final Long blockId, @QueryParam("offset") final Long offset, @QueryParam("length") final Long length, final byte[] data) { return RestUtils.call(new RestUtils.RestCallable<Void>() { @Override public Void call() throws Exception { Preconditions.checkNotNull(blockId, "required 'blockId' parameter is missing"); Preconditions.checkNotNull(sessionId, "required 'sessionId' parameter is missing"); Preconditions.checkNotNull(offset, "required 'offset' parameter is missing"); Preconditions.checkNotNull(length, "required 'length' parameter is missing"); Preconditions.checkState(offset >= 0, "invalid offset: %s", offset); Preconditions.checkState(length >= -1, "invalid length (except for -1): %s", length); // TODO(jiri): Wrap this logic in a block worker function; requires refactoring. ByteBuffer buffer = ByteBuffer.wrap(data); if (offset == 0) { // This is the first write to the block, so create the temp block file. The file will only // be created if the first write starts at offset 0. This allocates enough space for the // write. mBlockWorker.createBlockRemote(sessionId, blockId, mStorageTierAssoc.getAlias(0), length); } else { // Allocate enough space in the existing temporary block for the write. mBlockWorker.requestSpace(sessionId, blockId, length); } try (BlockWriter writer = mBlockWorker.getTempBlockWriterRemote(sessionId, blockId)) { writer.append(buffer); } return null; } }); } }