org.jboss.capedwarf.blobstore.CapedwarfBlobstoreService.java Source code

Java tutorial

Introduction

Here is the source code for org.jboss.capedwarf.blobstore.CapedwarfBlobstoreService.java

Source

/*
 * JBoss, Home of Professional Open Source.
 * Copyright 2011, Red Hat, Inc., and individual contributors
 * as indicated by the @author tags. See the copyright.txt file in the
 * distribution for a full listing of individual contributors.
 *
 * This 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 software 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.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this software; if not, write to the Free
 * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
 * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
 */

package org.jboss.capedwarf.blobstore;

import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.nio.channels.Channels;
import java.nio.channels.ReadableByteChannel;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.Part;

import com.google.appengine.api.blobstore.BlobInfo;
import com.google.appengine.api.blobstore.BlobKey;
import com.google.appengine.api.blobstore.BlobstoreFailureException;
import com.google.appengine.api.blobstore.ByteRange;
import com.google.appengine.api.blobstore.FileInfo;
import com.google.appengine.api.blobstore.RangeFormatException;
import com.google.appengine.api.blobstore.UnsupportedRangeFormatException;
import com.google.appengine.api.blobstore.UploadOptions;
import com.google.appengine.api.files.AppEngineFile;
import com.google.appengine.api.files.FileServiceFactory;
import com.google.appengine.api.files.FileWriteChannel;
import com.google.common.base.Function;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import org.jboss.capedwarf.common.io.IOUtils;
import org.jboss.capedwarf.common.servlet.ServletUtils;
import org.jboss.capedwarf.files.ExposedFileService;

/**
 * @author <a href="mailto:ales.justin@jboss.org">Ales Justin</a>
 * @author <a href="mailto:marko.luksa@gmail.com">Marko Luksa</a>
 */
public class CapedwarfBlobstoreService implements ExposedBlobstoreService {
    private static final String UPLOADED_BLOBKEY_ATTR = "com.google.appengine.api.blobstore.upload.blobkeys";
    private static final String UPLOADED_BLOBKEY_LIST_ATTR = "com.google.appengine.api.blobstore.upload.blobkeylists";

    private Function<List<BlobKey>, List<BlobInfo>> BLOB_LIST_KEY_TO_INFO_FN = new Function<List<BlobKey>, List<BlobInfo>>() {
        public List<BlobInfo> apply(List<BlobKey> input) {
            return Lists.transform(input, BLOB_KEY_TO_INFO_FN);
        }
    };

    private Function<BlobKey, BlobInfo> BLOB_KEY_TO_INFO_FN = new Function<BlobKey, BlobInfo>() {
        public BlobInfo apply(BlobKey input) {
            return getBlobInfo(input);
        }
    };

    private Function<List<BlobKey>, List<FileInfo>> FILE_LIST_KEY_TO_INFO_FN = new Function<List<BlobKey>, List<FileInfo>>() {
        public List<FileInfo> apply(List<BlobKey> input) {
            return Lists.transform(input, FILE_KEY_TO_INFO_FN);
        }
    };

    private Function<BlobKey, FileInfo> FILE_KEY_TO_INFO_FN = new Function<BlobKey, FileInfo>() {
        public FileInfo apply(BlobKey input) {
            return getFileService().getFileInfo(input);
        }
    };

    private ExposedFileService fileService;

    private synchronized ExposedFileService getFileService() {
        if (fileService == null) {
            fileService = (ExposedFileService) FileServiceFactory.getFileService();
        }
        return fileService;
    }

    public String createUploadUrl(String successPath) {
        return createUploadUrl(successPath, UploadOptions.Builder.withDefaults());
    }

    public String createUploadUrl(String successPath, UploadOptions uploadOptions) {
        return UploadServlet.createUploadUrl(successPath, uploadOptions);
    }

    public void delete(BlobKey... blobKeys) {
        getFileService().delete(blobKeys);
    }

    public void serve(BlobKey blobKey, HttpServletResponse response) throws IOException {
        serve(blobKey, (ByteRange) null, response);
    }

    public void serve(BlobKey blobKey, String rangeHeader, HttpServletResponse response) throws IOException {
        serve(blobKey, ByteRange.parse(rangeHeader), response);
    }

    public void serve(BlobKey blobKey, ByteRange byteRange, HttpServletResponse response) throws IOException {
        assertNotCommited(response);

        response.setStatus(HttpServletResponse.SC_OK);
        response.setHeader(BLOB_KEY_HEADER, blobKey.getKeyString());
        if (byteRange != null) {
            response.setHeader(BLOB_RANGE_HEADER, byteRange.toString());
        }
    }

    public void serveBlob(BlobKey blobKey, String byteRangeStr, HttpServletResponse response) throws IOException {
        assertNotCommited(response);

        BlobInfo blobInfo = getBlobInfo(blobKey);
        response.setContentType(blobInfo.getContentType());

        ByteRange byteRange = null;

        if (byteRangeStr == null) {
            response.setStatus(HttpServletResponse.SC_OK);
        } else {
            try {
                byteRange = ByteRange.parse(byteRangeStr);
            } catch (RangeFormatException e) {
                response.setStatus(HttpServletResponse.SC_REQUESTED_RANGE_NOT_SATISFIABLE);
                return;
            }

            if (byteRange.getStart() >= blobInfo.getSize()) {
                response.setStatus(HttpServletResponse.SC_REQUESTED_RANGE_NOT_SATISFIABLE);
                return;
            }

            if (byteRange.hasEnd() && byteRange.getEnd() >= blobInfo.getSize()) {
                byteRange = new ByteRange(byteRange.getStart(), blobInfo.getSize() - 1);
            }

            response.setStatus(HttpServletResponse.SC_PARTIAL_CONTENT);
            response.setHeader("Content-Range",
                    "bytes " + byteRange.getStart() + "-" + byteRange.getEnd() + "/" + blobInfo.getSize());
        }

        try {
            InputStream in = getStream(blobKey);
            try {
                copyStream(in, response.getOutputStream(), byteRange);
            } finally {
                in.close();
            }
        } catch (FileNotFoundException e) {
            response.setStatus(HttpServletResponse.SC_NOT_FOUND);
        }
    }

    private BlobInfo getBlobInfo(BlobKey blobKey) {
        return getFileService().getBlobInfo(blobKey);
    }

    private void assertNotCommited(HttpServletResponse response) {
        if (response.isCommitted()) {
            throw new IllegalStateException("Response was already committed.");
        }
    }

    private void copyStream(InputStream in, OutputStream out, ByteRange range) throws IOException {
        if (range == null) {
            IOUtils.copyStream(in, out);
        } else {
            if (range.hasEnd()) {
                long length = range.getEnd() + 1 - range.getStart(); // end is inclusive, hence +1
                IOUtils.copyStream(in, out, range.getStart(), length);
            } else {
                IOUtils.copyStream(in, out, range.getStart());
            }
        }
    }

    public byte[] fetchData(BlobKey blobKey, long startIndex, long endIndex) {
        if (startIndex < 0) {
            throw new IllegalArgumentException("startIndex must be >= 0");
        }

        if (endIndex < startIndex) {
            throw new IllegalArgumentException("endIndex must be >= startIndex");
        }

        long fetchSize = endIndex - startIndex + 1;
        if (fetchSize > MAX_BLOB_FETCH_SIZE) {
            throw new IllegalArgumentException("Blob fetch size " + fetchSize
                    + " is larger than MAX_BLOB_FETCH_SIZE (" + MAX_BLOB_FETCH_SIZE + ")");
        }

        try {
            InputStream stream = getStream(blobKey);
            return IOUtils.toBytes(stream, startIndex, endIndex, true);
        } catch (FileNotFoundException e) {
            throw new IllegalArgumentException("Blob does not exist");
        } catch (IOException e) {
            throw new BlobstoreFailureException("An unexpected error occured", e);
        }
    }

    @SuppressWarnings("unchecked")
    public ByteRange getByteRange(HttpServletRequest request) {
        Enumeration<String> rangeHeaders = request.getHeaders("range");
        if (!rangeHeaders.hasMoreElements()) {
            return null;
        }

        String rangeHeader = rangeHeaders.nextElement();
        if (rangeHeaders.hasMoreElements()) {
            throw new UnsupportedRangeFormatException("Cannot accept multiple range headers.");
        }

        return ByteRange.parse(rangeHeader);
    }

    public void storeUploadedBlobs(HttpServletRequest request) throws IOException, ServletException {
        Map<String, BlobKey> map = new HashMap<String, BlobKey>();
        Map<String, List<BlobKey>> map2 = new HashMap<String, List<BlobKey>>();
        for (Part part : request.getParts()) {
            if (ServletUtils.isFile(part)) {
                BlobKey blobKey = storeUploadedBlob(part);
                String name = part.getName();
                map.put(name, blobKey);
                List<BlobKey> list = map2.get(name);
                if (list == null) {
                    list = new LinkedList<BlobKey>();
                    map2.put(name, list);
                }
                list.add(blobKey);
            }
        }

        request.setAttribute(UPLOADED_BLOBKEY_ATTR, map);
        request.setAttribute(UPLOADED_BLOBKEY_LIST_ATTR, map2);
    }

    private BlobKey storeUploadedBlob(Part part) throws IOException {
        ExposedFileService fileService = getFileService();
        AppEngineFile file = fileService.createNewBlobFile(part.getContentType(), ServletUtils.getFileName(part));

        ReadableByteChannel in = Channels.newChannel(part.getInputStream());
        try {
            FileWriteChannel out = fileService.openWriteChannel(file, true);
            try {
                IOUtils.copy(in, out);
            } finally {
                out.closeFinally();
            }
        } finally {
            in.close();
        }

        return fileService.getBlobKey(file);
    }

    @SuppressWarnings("unchecked")
    public Map<String, BlobKey> getUploadedBlobs(HttpServletRequest request) {
        Map<String, BlobKey> map = (Map<String, BlobKey>) request.getAttribute(UPLOADED_BLOBKEY_ATTR);
        if (map == null) {
            throw new IllegalStateException("Must be called from a blob upload callback request.");
        }
        return map;
    }

    @SuppressWarnings("unchecked")
    public Map<String, List<BlobKey>> getUploads(HttpServletRequest request) {
        Map<String, List<BlobKey>> map = (Map<String, List<BlobKey>>) request
                .getAttribute(UPLOADED_BLOBKEY_LIST_ATTR);
        if (map == null) {
            throw new IllegalStateException("Must be called from a blob upload callback request.");
        }
        return map;
    }

    public InputStream getStream(BlobKey blobKey) throws FileNotFoundException {
        return getFileService().getStream(blobKey);
    }

    public BlobKey createGsBlobKey(String name) {
        return null; // TODO
    }

    public Map<String, List<BlobInfo>> getBlobInfos(HttpServletRequest httpServletRequest) {
        return Maps.transformValues(getUploads(httpServletRequest), BLOB_LIST_KEY_TO_INFO_FN);
    }

    public Map<String, List<FileInfo>> getFileInfos(HttpServletRequest httpServletRequest) {
        return Maps.transformValues(getUploads(httpServletRequest), FILE_LIST_KEY_TO_INFO_FN);
    }
}