com.fujitsu.dc.core.bar.BarFileInstaller.java Source code

Java tutorial

Introduction

Here is the source code for com.fujitsu.dc.core.bar.BarFileInstaller.java

Source

/**
 * personium.io
 * Copyright 2014 FUJITSU LIMITED
 *
 * 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 com.fujitsu.dc.core.bar;

import java.io.File;
import java.io.FileDescriptor;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.SyncFailedException;
import java.io.UnsupportedEncodingException;
import java.util.Enumeration;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.zip.ZipException;

import javax.ws.rs.core.HttpHeaders;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.Response.ResponseBuilder;
import javax.ws.rs.core.UriInfo;

import org.apache.commons.compress.archivers.zip.ZipArchiveEntry;
import org.apache.commons.compress.archivers.zip.ZipFile;
import org.apache.commons.io.IOUtils;
import org.apache.http.HttpStatus;
import org.json.simple.JSONObject;
import org.json.simple.parser.ParseException;
import org.odata4j.expression.BoolCommonExpression;
import org.odata4j.producer.QueryInfo;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.fujitsu.dc.core.DcCoreConfig;
import com.fujitsu.dc.core.DcCoreException;
import com.fujitsu.dc.core.auth.AccessContext;
import com.fujitsu.dc.core.auth.CellPrivilege;
import com.fujitsu.dc.core.bar.jackson.JSONManifest;
import com.fujitsu.dc.core.model.Box;
import com.fujitsu.dc.core.model.Cell;
import com.fujitsu.dc.core.odata.DcODataProducer;
import com.fujitsu.dc.core.odata.DcOptionsQueryParser;
import com.fujitsu.dc.core.rs.odata.ODataEntityResource;
import com.fujitsu.dc.core.rs.odata.ODataResource;

/**
 * bar??.
 */
public class BarFileInstaller {
    /**
     * .
     */
    static Logger log = LoggerFactory.getLogger(BarFileInstaller.class);

    static final long MB = 1024 * 1024;
    static final int BUF_SIZE = 1024; // for output response.

    private final Cell cell;
    private String boxName;
    private ODataEntityResource oDataEntityResource;
    private UriInfo uriInfo;

    private String barTempDir = DcCoreConfig.get(DcCoreConfig.BAR.BAR_INSTALLFILE_DIR);

    private JSONObject manifestJson;

    /**
     * .
     * @param cell
     *            
     * @param boxName
     *            ??
     * @param oDataEntityResource oDataEntityResource
     * @param uriInfo UriInfo
     */
    public BarFileInstaller(final Cell cell, final String boxName, final ODataEntityResource oDataEntityResource,
            final UriInfo uriInfo) {
        this.cell = cell;
        this.boxName = boxName;
        this.oDataEntityResource = oDataEntityResource;
        this.uriInfo = uriInfo;
    }

    /**
     * bar?.
     * @param headers
     *            Http???MAP
     * @param inStream
     *            HttpInputStream
     * @param requestKey ??RequestKey?
     * @return ?
     */
    public Response barFileInstall(Map<String, String> headers, InputStream inStream, String requestKey) {

        // ??
        checkPreConditions(headers);

        // bar??
        File barFile = storeTemporaryBarFile(inStream);

        BarFileReadRunner runner = null;
        try {
            // bar??
            long entryCount = checkBarFileContents(barFile);

            // Box??URL???
            checkDuplicateBoxAndSchema();

            // Box??
            // ??????400?????Box????????Box????????
            runner = new BarFileReadRunner(barFile, this.cell, this.boxName, this.oDataEntityResource,
                    this.oDataEntityResource.getOdataProducer(), Box.EDM_TYPE_NAME, this.uriInfo, requestKey);
            runner.createBox(this.manifestJson);

            // bar????ProgressInfo?
            runner.setEntryCount(entryCount);
            runner.writeInitProgressCache();

        } catch (DcCoreException e) {
            if (null != runner) {
                runner.writeErrorProgressCache();
            }
            removeBarFile(barFile);
            throw e;
        } catch (Exception e) {
            if (null != runner) {
                runner.writeErrorProgressCache();
            }
            removeBarFile(barFile);
            throw DcCoreException.Server.UNKNOWN_ERROR;
        } finally {
            IOUtils.closeQuietly(inStream);
        }

        // ??
        // TODO ?????????
        Thread thread = new Thread(runner);
        thread.start();

        // ???
        ResponseBuilder res = Response.status(HttpStatus.SC_ACCEPTED);
        res.header(HttpHeaders.LOCATION, this.cell.getUrl() + boxName);
        return res.build();
    }

    private void removeBarFile(File barFile) {
        if (barFile.exists() && !barFile.delete()) {
            log.warn("Failed to remove bar file. [" + barFile.getAbsolutePath() + "].");
        }
    }

    /**
     * bar?????.
     * @param headers HTTP
     */
    private void checkPreConditions(Map<String, String> headers) {
        // [403]
        AccessContext accessContext = this.oDataEntityResource.getAccessContext();
        ODataResource odataResource = this.oDataEntityResource.getOdataResource();
        odataResource.checkAccessContext(accessContext, CellPrivilege.BOX_BAR_INSTALL);

        // [400]???
        checkHeaders(headers);

    }

    /**
     * Http??.
     * @param headers
     *            Http???MAP
     */
    private void checkHeaders(Map<String, String> headers) {
        // Content-Type: application/zip
        String contentType = headers.get(HttpHeaders.CONTENT_TYPE);
        if (!"application/zip".equals(contentType)) {
            throw DcCoreException.BarInstall.REQUEST_HEADER_FORMAT_ERROR.params(HttpHeaders.CONTENT_TYPE);
        }
    }

    /**
     * ????bar?(MB)??
     * @return com.fujitsu.dc.core.bar.file.maxSize
     */
    protected long getMaxBarFileSize() {
        long maxBarFileSize;
        try {
            maxBarFileSize = Long.parseLong(DcCoreConfig.get(DcCoreConfig.BAR.BAR_FILE_MAX_SIZE));
        } catch (NumberFormatException ne) {
            throw DcCoreException.Server.UNKNOWN_ERROR;
        }
        return maxBarFileSize;
    }

    /**
     * ?BAR?(MB)??
     * @return com.fujitsu.dc.core.bar.entry.maxSize
     */
    protected long getMaxBarEntryFileSize() {
        long maxBarFileSize;
        try {
            maxBarFileSize = Long.parseLong(DcCoreConfig.get(DcCoreConfig.BAR.BAR_ENTRY_MAX_SIZE));
        } catch (NumberFormatException ne) {
            log.info("NumberFormatException" + DcCoreConfig.get(DcCoreConfig.BAR.BAR_ENTRY_MAX_SIZE));
            throw DcCoreException.Server.UNKNOWN_ERROR;
        }
        return maxBarFileSize;
    }

    /**
     * ??.
     * @param fd 
     * @throws SyncFailedException ??
     */
    public void sync(FileDescriptor fd) throws SyncFailedException {
        fd.sync();
    }

    /**
     * Http?bar??????.
     * @param inStream HttpInputStream
     * @return ????bar?File
     */
    private File storeTemporaryBarFile(InputStream inStream) {

        // bar?????????
        String unitUserName = BarFileUtils.getUnitUserName(this.cell.getOwner());
        File barFileDir = new File(new File(barTempDir, unitUserName), "bar");
        if (!barFileDir.exists() && !barFileDir.mkdirs()) {
            String message = "unable create directory: " + barFileDir.getAbsolutePath();
            throw DcCoreException.Server.FILE_SYSTEM_ERROR.params(message);
        }

        // barNFS???
        String prefix = this.cell.getId() + "_" + this.boxName;
        File barFile = null;
        OutputStream outStream = null;
        try {
            barFile = File.createTempFile(prefix, ".bar", barFileDir);
            barFile.deleteOnExit(); // VM??
            outStream = new FileOutputStream(barFile);
            IOUtils.copyLarge(inStream, outStream);
        } catch (IOException e) {
            String message = "unable save bar file: %s";
            if (barFile == null) {
                message = String.format(message, barFileDir + prefix + "XXX.bar");
            } else {
                message = String.format(message, barFile.getAbsolutePath());
            }
            throw DcCoreException.Server.FILE_SYSTEM_ERROR.params(message);
        } finally {
            if (null != outStream && DcCoreConfig.getFsyncEnabled()) {
                try {
                    sync(((FileOutputStream) outStream).getFD());
                } catch (Exception e) {
                    throw DcCoreException.Server.FILE_SYSTEM_ERROR.params(e.getMessage());
                }
            }
            IOUtils.closeQuietly(outStream);
        }
        return barFile;
    }

    /**
     * bar?????.
     * <ul>
     * <li>bar????</li>
     * <li>bar???????</li>
     * <li>TODO bar??????</li>
     * </ul>.
     * @param barFile ????bar?File
     * @returns bar?
     */
    private long checkBarFileContents(File barFile) {

        // bar?
        checkBarFileSize(barFile);

        ZipFile zipFile = null;
        try {
            zipFile = new ZipFile(barFile, "UTF-8");
            Enumeration<ZipArchiveEntry> entries = zipFile.getEntries();
            ZipArchiveEntry zae = null;
            long entryCount = 0;
            String entryName = null;
            try {
                long maxBarEntryFileSize = getMaxBarEntryFileSize();
                // ??
                Map<String, String> requiredBarFiles = setupBarFileOrder();
                while (entries.hasMoreElements()) {
                    zae = entries.nextElement();
                    entryName = zae.getName();
                    log.info("read: " + entryName);
                    if (!zae.isDirectory()) {
                        // ??????bar?
                        entryCount++;

                        // bar??
                        checkBarFileEntrySize(zae, entryName, maxBarEntryFileSize);

                        // Box?????
                        if (zae.getName().endsWith("/" + BarFileReadRunner.MANIFEST_JSON)) {
                            checkAndReadManifest(entryName, zae, zipFile);
                        }
                    }
                    // bar???????
                    if (!checkBarFileStructures(zae, requiredBarFiles)) {
                        throw DcCoreException.BarInstall.BAR_FILE_INVALID_STRUCTURES.params(entryName);
                    }
                }
                if (!requiredBarFiles.isEmpty()) {
                    StringBuilder entryNames = new StringBuilder();
                    Object[] requiredFileNames = requiredBarFiles.keySet().toArray();
                    for (int i = 0; i < requiredFileNames.length; i++) {
                        if (i > 0) {
                            entryNames.append(" " + requiredFileNames[i]);
                        } else {
                            entryNames.append(requiredFileNames[i]);
                        }
                    }
                    throw DcCoreException.BarInstall.BAR_FILE_INVALID_STRUCTURES.params(entryNames.toString());
                }
                return entryCount;
            } catch (DcCoreException e) {
                throw e;
            } catch (Exception e) {
                log.info(e.getMessage(), e.fillInStackTrace());
                throw DcCoreException.BarInstall.BAR_FILE_CANNOT_READ.params(entryName);
            }
        } catch (FileNotFoundException e) {
            throw DcCoreException.BarInstall.BAR_FILE_CANNOT_OPEN.params("barFile");
        } catch (ZipException e) {
            throw DcCoreException.BarInstall.BAR_FILE_CANNOT_OPEN.params(e.getMessage());
        } catch (IOException e) {
            throw DcCoreException.BarInstall.BAR_FILE_CANNOT_OPEN.params(e.getMessage());
        } catch (DcCoreException e) {
            throw e;
        } catch (RuntimeException e) {
            throw DcCoreException.Server.UNKNOWN_ERROR;
        } finally {
            ZipFile.closeQuietly(zipFile);
        }
    }

    private void checkAndReadManifest(String entryName, ZipArchiveEntry zae, ZipFile zipFile) throws IOException {
        InputStream inStream = zipFile.getInputStream(zae);
        try {
            JSONManifest manifest = BarFileUtils.readJsonEntry(inStream, entryName, JSONManifest.class);
            if (!manifest.checkSchema()) {
                throw DcCoreException.BarInstall.BAR_FILE_INVALID_STRUCTURES.params(entryName);
            }
            this.manifestJson = manifest.getJson();
        } finally {
            IOUtils.closeQuietly(inStream);
        }
    }

    /**
     * bar??.
     * @param zae bar
     * @param entryName ??
     * @param maxBarEntryFileSize ?
     */
    protected void checkBarFileEntrySize(ZipArchiveEntry zae, String entryName, long maxBarEntryFileSize) {
        // [400]bar??????
        if (zae.getSize() > (long) (maxBarEntryFileSize * MB)) {
            String message = "Bar file entry size too large invalid file [%s: %sB]";
            log.info(String.format(message, entryName, String.valueOf(zae.getSize())));
            throw DcCoreException.BarInstall.BAR_FILE_ENTRY_SIZE_TOO_LARGE.params(entryName,
                    String.valueOf(zae.getSize()));
        }
    }

    /**
     * bar?.
     * @param barFile bar
     */
    protected void checkBarFileSize(File barFile) {
        // [400]bar??????
        long maxBarFileSize = getMaxBarFileSize();
        if (barFile.length() > (long) (maxBarFileSize * MB)) {
            String message = "Bar file size too large invalid file [%sB]";
            log.info(String.format(message, String.valueOf(barFile.length())));
            throw DcCoreException.BarInstall.BAR_FILE_SIZE_TOO_LARGE.params(String.valueOf(barFile.length()));
        }
    }

    /**
     * bar?.
     */
    private Map<String, String> setupBarFileOrder() {
        Map<String, String> requiredBarFiles = new LinkedHashMap<String, String>();
        requiredBarFiles.put("bar/", BarFileReadRunner.ROOT_DIR);
        requiredBarFiles.put("bar/00_meta/", BarFileReadRunner.META_DIR);
        requiredBarFiles.put("bar/00_meta/00_manifest.json", BarFileReadRunner.MANIFEST_JSON);
        requiredBarFiles.put("bar/00_meta/90_rootprops.xml", BarFileReadRunner.ROOTPROPS_XML);
        return requiredBarFiles;
    }

    /**
     * bar???.
     */
    private boolean checkBarFileStructures(ZipArchiveEntry zae, Map<String, String> requiredBarFiles)
            throws UnsupportedEncodingException, ParseException {

        String entryName = zae.getName(); // ex. "bar/00_meta/00_manifest.json"
        if (requiredBarFiles.containsKey(entryName)) {
            requiredBarFiles.remove(entryName);
        }
        return true;
    }

    /**
     * Box??????????????URL???????????.
     */
    private void checkDuplicateBoxAndSchema() {
        DcODataProducer producer = oDataEntityResource.getOdataProducer();

        // [400]???scheme URL???Box????
        // ??URL??Box??1??????
        String schemaUrl = (String) this.manifestJson.get("Schema");
        BoolCommonExpression filter = DcOptionsQueryParser.parseFilter("Schema eq '" + schemaUrl + "'");
        QueryInfo query = new QueryInfo(null, null, null, filter, null, null, null, null, null);
        if (producer.getEntitiesCount(Box.EDM_TYPE_NAME, query).getCount() > 0) {
            throw DcCoreException.BarInstall.BAR_FILE_BOX_SCHEMA_ALREADY_EXISTS.params(schemaUrl);
        }

        // [405]?????Box????
        // Box?????????????????
        filter = DcOptionsQueryParser.parseFilter("Name eq '" + this.boxName + "'");
        query = new QueryInfo(null, null, null, filter, null, null, null, null, null);
        if (producer.getEntitiesCount(Box.EDM_TYPE_NAME, query).getCount() > 0) {
            throw DcCoreException.BarInstall.BAR_FILE_BOX_ALREADY_EXISTS.params(this.boxName);
        }

        log.info("Install target Box is not found, able to install.");
    }

    /**
     * ????.
     * @return ??
     */
    public String getCellName() {
        return cell.getName();
    }

    /**
     * ODataProducer??.
     * @return ODataProducer
     */
    public DcODataProducer getOdataProducer() {
        return oDataEntityResource.getOdataProducer();
    }

    /**
     * ODataEntityResource??.
     * @return ODataEntityResource
     */
    public ODataEntityResource getODataEntityResource() {
        return oDataEntityResource;
    }

    /**
     * URI??.
     * @return UriInfo
     */
    public UriInfo getUriInfo() {
        return uriInfo;
    }

}