org.robotbrains.support.web.server.netty.NettyHttpFileUpload.java Source code

Java tutorial

Introduction

Here is the source code for org.robotbrains.support.web.server.netty.NettyHttpFileUpload.java

Source

/*
 * Copyright (C) 2012 Google Inc.
 * Copyright (C) 2015 Keith M. Hughes.
 *
 * 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 org.robotbrains.support.web.server.netty;

import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.http.HttpChunkedInput;
import io.netty.handler.codec.http.HttpRequest;
import io.netty.handler.codec.http.HttpResponseStatus;
import io.netty.handler.codec.http.multipart.Attribute;
import io.netty.handler.codec.http.multipart.FileUpload;
import io.netty.handler.codec.http.multipart.HttpPostMultipartRequestDecoder;
import io.netty.handler.codec.http.multipart.HttpPostRequestDecoder.EndOfDataDecoderException;
import io.netty.handler.codec.http.multipart.InterfaceHttpData;
import io.netty.handler.codec.http.multipart.InterfaceHttpData.HttpDataType;
import io.smartspaces.SmartSpacesException;

import java.io.File;
import java.io.IOException;
import java.io.OutputStream;
import java.net.HttpCookie;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;

import org.apache.logging.log4j.Logger;
import org.robotbrains.support.web.server.HttpFileUpload;

/**
 * A Netty-based {@link HttpFileUpload}.
 *
 * @author Keith M. Hughes
 */
public class NettyHttpFileUpload implements HttpFileUpload {

    /**
     * The handler for the POST request. It can be {@code null}.
     */
    private NettyHttpPostRequestHandler handler;

    /**
     * The parameters that were part of the post.
     */
    private Map<String, String> parameters = new HashMap<>();

    /**
     * The decoder.
     */
    private HttpPostMultipartRequestDecoder decoder;

    /**
     * The request that started everything off.
     */
    private HttpRequest nettyHttpRequest;

    /**
     * The web server handler handling the request.
     */
    private NettyWebServerHandler webServerHandler;

    /**
     * FileUpload container for this particular upload.
     */
    private FileUpload fileUpload;

    /**
     * The cookies to add.
     */
    private Set<HttpCookie> cookies;

    /**
     * Create a new instance.
     *
     * @param nettyHttpRequest
     *          incoming Netty HTTP request
     * @param decoder
     *          decoder to use
     * @param handler
     *          the HTTP POST handler, can be {@code null}
     * @param webServerHandler
     *          underlying web server handler
     * @param cookies
     *          any cookies to add to responses
     */
    public NettyHttpFileUpload(HttpRequest nettyHttpRequest, HttpPostMultipartRequestDecoder decoder,
            NettyHttpPostRequestHandler handler, NettyWebServerHandler webServerHandler, Set<HttpCookie> cookies) {
        this.nettyHttpRequest = nettyHttpRequest;
        this.decoder = decoder;
        this.handler = handler;
        this.webServerHandler = webServerHandler;
        this.cookies = cookies;
    }

    /**
     * Get the original Netty HTTP request.
     *
     * @return the original Netty HTTP request
     */
    public HttpRequest getNettyHttpRequest() {
        return nettyHttpRequest;
    }

    /**
     * Add a new chunk of data to the upload.
     *
     * @param ctx
     *          the context for the channel handling
     * @param chunk
     *          the chunked data
     *
     * @throws Exception
     *           problem adding chunk
     *
     */
    public void addChunk(ChannelHandlerContext ctx, HttpChunkedInput chunk) throws Exception {
        decoder.offer(chunk.readChunk(ctx));
        try {
            while (decoder.hasNext()) {
                InterfaceHttpData data = decoder.next();
                if (data != null) {
                    processHttpData(data);
                }
                if (chunk.isEndOfInput()) {
                    break;
                }
            }
        } catch (EndOfDataDecoderException e) {
            getLog().error("Error while adding HTTP chunked POST data", e);
        }
    }

    /**
     * Clean up anything from the upload.
     */
    public void clean() {
        decoder.cleanFiles();
    }

    /**
     * Complete processing of the file upload.
     */
    public void completeNonChunked() {
        try {
            for (InterfaceHttpData data : decoder.getBodyHttpDatas()) {
                processHttpData(data);
            }
        } catch (Exception e) {
            getLog().error("Error while completing HTTP chunked POST data", e);
        }
    }

    /**
     * The file upload is complete. Handle it as needed.
     *
     * @param context
     *          the context for the channel handler
     */
    public void fileUploadComplete(ChannelHandlerContext context) {
        try {
            handler.handleWebRequest(context, nettyHttpRequest, this, cookies);
            getLog().debug("HTTP [%s] %s --> (File Upload)", HttpResponseStatus.OK, nettyHttpRequest.getUri());
        } catch (Exception e) {
            getLog().error(String.format("Exception when handling web request %s", nettyHttpRequest.getUri()), e);
            webServerHandler.sendError(context, HttpResponseStatus.INTERNAL_SERVER_ERROR);
        } finally {
            clean();
        }
    }

    /**
     * Process a clump of of the HTTP data.
     *
     * @param data
     *          the data
     */
    private void processHttpData(InterfaceHttpData data) {
        if (data.getHttpDataType() == HttpDataType.Attribute) {
            Attribute attribute = (Attribute) data;
            try {
                parameters.put(attribute.getName(), attribute.getValue());
            } catch (IOException e1) {
                // Error while reading data from File, only print name and error
                getLog().error("Form post BODY Attribute: " + attribute.getHttpDataType().name() + ": "
                        + attribute.getName() + " Error while reading value:", e1);
            }
        } else if (data.getHttpDataType() == HttpDataType.FileUpload) {
            fileUpload = (FileUpload) data;
            if (fileUpload.isCompleted()) {
                getLog().info("File %s uploaded", fileUpload.getFilename());
            } else {
                getLog().error("File to be continued but should not!");
            }
        } else {
            getLog().warn("Unprocessed form post data type %s", data.getHttpDataType().name());
        }
    }

    @Override
    public boolean hasFile() {
        return fileUpload != null;
    }

    @Override
    public boolean moveTo(File destination) {
        if (hasFile()) {
            try {
                fileUpload.renameTo(destination);

                return true;
            } catch (Exception e) {
                throw SmartSpacesException.newFormattedException(e, "Unable to save uploaded file to %s",
                        destination);
            }
        } else {
            return false;
        }
    }

    @Override
    public boolean copyTo(OutputStream destination) {
        if (hasFile()) {
            try {
                ByteBuf buffer = fileUpload.content();
                buffer.getBytes(0, destination, buffer.readableBytes());

                return true;
            } catch (Exception e) {
                throw SmartSpacesException.newFormattedException(e,
                        "Unable to save uploaded file to output stream");
            }
        } else {
            return false;
        }
    }

    @Override
    public String getFormName() {
        return fileUpload.getName();
    }

    @Override
    public String getFilename() {
        if (hasFile()) {
            return fileUpload.getFilename();
        } else {
            return null;
        }
    }

    @Override
    public Map<String, String> getParameters() {
        return parameters;
    }

    /**
     * Get the log for the uploader.
     *
     * @return the log
     */
    private Logger getLog() {
        return webServerHandler.getWebServer().getLog();
    }
}