/** * Copyright (c) 2000-present Liferay, Inc. All rights reserved. * * This library is free software; you can redistribute it and/or modify it under * the terms of the GNU Lesser General Public License as published by the Free * Software Foundation; either version 2.1 of the License, or (at your option) * any later version. * * This library is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more * details. */ package com.liferay.portal.fabric.netty.fileserver.handlers; import com.liferay.portal.fabric.netty.fileserver.FileHelperUtil; import com.liferay.portal.fabric.netty.fileserver.FileResponse; import com.liferay.portal.kernel.concurrent.AsyncBroker; import com.liferay.portal.kernel.log.Log; import com.liferay.portal.kernel.log.LogFactoryUtil; import io.netty.buffer.ByteBuf; import io.netty.channel.ChannelHandlerContext; import io.netty.channel.ChannelInboundHandlerAdapter; import io.netty.channel.ChannelPipeline; import io.netty.util.concurrent.EventExecutor; import java.io.IOException; import java.nio.channels.FileChannel; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.StandardOpenOption; import java.nio.file.attribute.FileTime; import java.util.concurrent.Callable; /** * @author Shuyang Zhou */ public class FileUploadChannelHandler extends ChannelInboundHandlerAdapter { public FileUploadChannelHandler( AsyncBroker<Path, FileResponse> asyncBroker, FileResponse fileResponse, EventExecutor eventExecutor) throws IOException { if (asyncBroker == null) { throw new NullPointerException("Async broker is null"); } if (fileResponse == null) { throw new NullPointerException("File response is null"); } if (eventExecutor == null) { throw new NullPointerException("Event executor is null"); } if (fileResponse.getSize() < 1) { throw new IllegalArgumentException( "File response has no content for uploading"); } this.asyncBroker = asyncBroker; this.fileResponse = fileResponse; this.eventExecutor = eventExecutor; tempFilePath = Files.createTempFile( FileUploadChannelHandler.class.getName() + "-", null); fileChannel = FileChannel.open( tempFilePath, StandardOpenOption.WRITE, StandardOpenOption.TRUNCATE_EXISTING); fileResponse.setLocalFile(tempFilePath); } @Override public void channelRead( ChannelHandlerContext channelHandlerContext, Object object) throws IOException { ByteBuf byteBuf = (ByteBuf)object; if (!receive(byteBuf)) { return; } fileChannel.close(); if (eventExecutor.inEventLoop() || !fileResponse.isFolder()) { finish(); } else { eventExecutor.submit( new Callable<Void>() { @Override public Void call() throws IOException { try { finish(); } catch (IOException ioe) { exceptionCaught(null, ioe); } return null; } }); } ChannelPipeline channelPipeline = channelHandlerContext.pipeline(); channelPipeline.remove(this); if (byteBuf.isReadable()) { channelHandlerContext.fireChannelRead(object); } } @Override public void exceptionCaught( ChannelHandlerContext channelHandlerContext, Throwable throwable) throws IOException { _log.error("File upload failure", throwable); if (channelHandlerContext != null) { ChannelPipeline channelPipeline = channelHandlerContext.pipeline(); channelPipeline.remove(this); } if (!asyncBroker.takeWithException(fileResponse.getPath(), throwable)) { _log.error( "Unable to place exception because no future exists with ID " + fileResponse.getPath(), throwable); } fileChannel.close(); Files.delete(tempFilePath); } protected void finish() throws IOException { if (fileResponse.isFolder()) { fileResponse.setLocalFile( FileHelperUtil.unzip( tempFilePath, FileHelperUtil.TEMP_DIR_PATH)); Files.delete(tempFilePath); } Files.setLastModifiedTime( fileResponse.getLocalFile(), FileTime.fromMillis(fileResponse.getLastModifiedTime())); Path path = fileResponse.getPath(); if (!asyncBroker.takeWithResult(path, fileResponse)) { _log.error( "Unable to place result " + fileResponse + " because no future exists with ID " + path); } } protected boolean receive(ByteBuf byteBuf) throws IOException { while (true) { long readSize = fileResponse.getSize() - fileChannel.position(); int readableBytes = byteBuf.readableBytes(); if (readSize > readableBytes) { readSize = readableBytes; } if (byteBuf.readBytes(fileChannel, (int)readSize) == readSize) { break; } } if (!byteBuf.isReadable()) { byteBuf.release(); } if (fileChannel.position() < fileResponse.getSize()) { return false; } return true; } protected final AsyncBroker<Path, FileResponse> asyncBroker; protected final EventExecutor eventExecutor; protected final FileChannel fileChannel; protected final FileResponse fileResponse; protected final Path tempFilePath; private static final Log _log = LogFactoryUtil.getLog( FileUploadChannelHandler.class); }