io.personium.core.bar.BarFileReadRunner.java Source code

Java tutorial

Introduction

Here is the source code for io.personium.core.bar.BarFileReadRunner.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 io.personium.core.bar;

import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.Reader;
import java.io.StringReader;
import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.UriInfo;
import javax.ws.rs.ext.RuntimeDelegate;

import org.apache.commons.compress.archivers.zip.ZipArchiveEntry;
import org.apache.commons.compress.archivers.zip.ZipArchiveInputStream;
import org.apache.commons.io.IOUtils;
import org.apache.commons.io.input.CloseShieldInputStream;
import org.apache.wink.webdav.WebDAVMethod;
import org.apache.wink.webdav.model.Getcontenttype;
import org.apache.wink.webdav.model.Multistatus;
import org.apache.wink.webdav.model.Prop;
import org.apache.wink.webdav.model.Propertyupdate;
import org.apache.wink.webdav.model.Propstat;
import org.apache.wink.webdav.model.Resourcetype;
import org.apache.wink.webdav.model.Response;
import org.codehaus.jackson.JsonFactory;
import org.codehaus.jackson.JsonParseException;
import org.codehaus.jackson.JsonParser;
import org.codehaus.jackson.JsonToken;
import org.codehaus.jackson.map.JsonMappingException;
import org.codehaus.jackson.map.ObjectMapper;
import org.codehaus.jackson.map.exc.UnrecognizedPropertyException;
import org.json.simple.JSONObject;
import org.odata4j.core.OEntity;
import org.odata4j.core.OEntityId;
import org.odata4j.core.OEntityIds;
import org.odata4j.core.OEntityKey;
import org.odata4j.edm.EdmAssociation;
import org.odata4j.edm.EdmAssociationEnd;
import org.odata4j.edm.EdmComplexType;
import org.odata4j.edm.EdmDataServices;
import org.odata4j.edm.EdmEntityType;
import org.odata4j.edm.EdmProperty;
import org.odata4j.edm.EdmProperty.CollectionKind;
import org.odata4j.edm.EdmStructuralType;
import org.odata4j.producer.EntityResponse;
import org.odata4j.stax2.XMLEventReader2;
import org.odata4j.stax2.XMLFactoryProvider2;
import org.odata4j.stax2.XMLInputFactory2;
import org.odata4j.stax2.staximpl.StaxXMLFactoryProvider2;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.w3c.dom.Element;

import io.personium.common.es.util.PersoniumUUID;
import io.personium.common.utils.PersoniumCoreUtils;
import io.personium.core.PersoniumCoreException;
import io.personium.core.PersoniumCoreMessageUtils;
import io.personium.core.PersoniumUnitConfig;
import io.personium.core.bar.jackson.JSONExtRoles;
import io.personium.core.bar.jackson.JSONLinks;
import io.personium.core.bar.jackson.JSONManifest;
import io.personium.core.bar.jackson.JSONMappedObject;
import io.personium.core.bar.jackson.JSONRelations;
import io.personium.core.bar.jackson.JSONRoles;
import io.personium.core.bar.jackson.JSONUserDataLinks;
import io.personium.core.eventbus.JSONEvent;
import io.personium.core.eventbus.PersoniumEventBus;
import io.personium.core.model.Box;
import io.personium.core.model.BoxCmp;
import io.personium.core.model.Cell;
import io.personium.core.model.DavCmp;
import io.personium.core.model.DavCommon;
import io.personium.core.model.ModelFactory;
import io.personium.core.model.ctl.AssociationEnd;
import io.personium.core.model.ctl.ComplexType;
import io.personium.core.model.ctl.ComplexTypeProperty;
import io.personium.core.model.ctl.CtlSchema;
import io.personium.core.model.ctl.EntityType;
import io.personium.core.model.ctl.Event;
import io.personium.core.model.ctl.Event.LEVEL;
import io.personium.core.model.ctl.ExtRole;
import io.personium.core.model.ctl.Property;
import io.personium.core.model.ctl.Relation;
import io.personium.core.model.ctl.Role;
import io.personium.core.model.impl.es.doc.EntitySetDocHandler;
import io.personium.core.model.impl.es.odata.UserDataODataProducer;
import io.personium.core.model.impl.es.odata.UserSchemaODataProducer;
import io.personium.core.model.progress.Progress;
import io.personium.core.model.progress.ProgressInfo;
import io.personium.core.model.progress.ProgressManager;
import io.personium.core.odata.OEntityWrapper;
import io.personium.core.odata.PersoniumEdmxFormatParser;
import io.personium.core.odata.PersoniumODataProducer;
import io.personium.core.rs.cell.EventResource;
import io.personium.core.rs.odata.BulkRequest;
import io.personium.core.rs.odata.ODataEntitiesResource;
import io.personium.core.rs.odata.ODataEntityResource;
import io.personium.core.rs.odata.ODataResource;

/**
 * Http?bar????.
 */
public class BarFileReadRunner implements Runnable {

    private static final String CODE_BAR_INSTALL_FAILED = "PL-BI-0001";

    private static final String CODE_BAR_INSTALL_COMPLETED = "PL-BI-0000";

    private static final String CODE_BAR_INSTALL_STARTED = "PL-BI-1001";

    /**
     * .
     */
    static Logger log = LoggerFactory.getLogger(BarFileReadRunner.class);

    static final long MB = 1024 * 1024;
    private static final int TYPE_WEBDAV_COLLECTION = 0;
    private static final int TYPE_ODATA_COLLECTION = 1;
    private static final int TYPE_SERVICE_COLLECTION = 2;
    private static final int TYPE_DAV_FILE = 3;
    private static final int TYPE_SVC_FILE = 4;
    private static final int TYPE_MISMATCH = -1;

    private Map<String, Boolean> barFileOrder;
    private File barFile;
    private ZipArchiveInputStream zipArchiveInputStream;
    private final String boxName;
    private final ODataEntityResource odataEntityResource;
    private final PersoniumODataProducer odataProducer;
    private final String entitySetName;
    private final UriInfo uriInfo;
    private final String requestKey;

    static final String ROOT_DIR = "bar/";
    static final String META_DIR = "bar/00_meta/";
    static final String CONTENTS_DIR_NAME = "90_contents";
    static final String CONTENTS_DIR = ROOT_DIR + CONTENTS_DIR_NAME + "/";
    static final String MANIFEST_JSON = "00_manifest.json";
    static final String RELATION_JSON = "10_relations.json";
    static final String ROLE_JSON = "20_roles.json";
    static final String EXTROLE_JSON = "30_extroles.json";
    static final String LINKS_JSON = "70_$links.json";
    static final String ROOTPROPS_XML = "90_rootprops.xml";
    static final String METADATA_XML = "00_$metadata.xml";
    static final String USERDATA_LINKS_JSON = "10_odatarelations.json";
    static final String USERDATA_DIR_NAME = "90_data";

    private static final String DCBOX_NO_SLUSH = "dcbox:";
    private static final String DCBOX = "dcbox:/";

    private Cell cell;
    private Box box;
    private String schemaUrl; // ACL????
    private BoxCmp boxCmp;
    private Map<String, DavCmp> davCmpMap;
    private Map<String, String> davFileMap = new HashMap<String, String>();
    private long linksOutputStreamSize = Long
            .parseLong(PersoniumUnitConfig.get(PersoniumUnitConfig.BAR.BAR_USERDATA_LINKS_OUTPUT_STREAM_SIZE));
    private long bulkSize = Long.parseLong(PersoniumUnitConfig.get(PersoniumUnitConfig.BAR.BAR_USERDATA_BULK_SIZE));
    private Event event;
    private PersoniumEventBus eventBus;
    private BarInstallProgressInfo progressInfo;

    /**
     * .
     * @param barFile bar file object
     * @param cell Install target Cell
     * @param boxName Install target Box Name
     * @param odataEntityResource JAX-RS resource
     * @param producer ODataProducer
     * @param entitySetName entitySetName(=box name)
     * @param uriInfo uriInfo
     * @param requestKey ??RequestKey?
     */
    public BarFileReadRunner(File barFile, Cell cell, String boxName, ODataEntityResource odataEntityResource,
            PersoniumODataProducer producer, String entitySetName, UriInfo uriInfo, String requestKey) {
        this.barFile = barFile;
        this.boxName = boxName;
        this.odataEntityResource = odataEntityResource;
        this.odataProducer = producer;
        this.entitySetName = entitySetName;
        this.uriInfo = uriInfo;
        this.cell = cell;
        this.box = null;
        this.boxCmp = null;
        this.davCmpMap = new HashMap<String, DavCmp>();
        this.requestKey = requestKey;
        setupBarFileOrder();
    }

    /**
     * bar???.
     */
    public void run() {
        boolean isSuccess = true;

        String path = "/" + this.cell.getName() + "/" + boxName + "/";
        log.debug("install target: " + path);

        try {
            List<String> doneKeys = new ArrayList<String>();

            try {
                this.zipArchiveInputStream = new ZipArchiveInputStream(new FileInputStream(barFile));
            } catch (IOException e) {
                throw PersoniumCoreException.Server.FILE_SYSTEM_ERROR.params(e.getMessage());
            }
            // ("bar/")??
            if (!isRootDir()) {
                String message = PersoniumCoreMessageUtils.getMessage("PL-BI-2001");
                writeOutputStream(true, "PL-BI-1004", ROOT_DIR, message);
                isSuccess = false;
                return;
            }

            // 00_meta??
            if (!isMetadataDir()) {
                String message = PersoniumCoreMessageUtils.getMessage("PL-BI-2001");
                writeOutputStream(true, "PL-BI-1004", META_DIR, message);
                isSuccess = false;
                return;
            }

            // 00_meta???
            ZipArchiveEntry zae = null;
            try {
                long maxBarEntryFileSize = getMaxBarEntryFileSize();
                Set<String> keyList = barFileOrder.keySet();

                while ((zae = this.zipArchiveInputStream.getNextZipEntry()) != null) {
                    String entryName = zae.getName();
                    log.debug("Entry Name: " + entryName);
                    log.debug("Entry Size: " + zae.getSize());
                    log.debug("Entry Compressed Size: " + zae.getCompressedSize());
                    if (!zae.isDirectory()) {
                        this.progressInfo.addDelta(1L);
                    }

                    // bar??
                    isSuccess = createMetadata(zae, entryName, maxBarEntryFileSize, keyList, doneKeys);
                    if (!isSuccess) {
                        break;
                    }
                    // 90_contents??????
                    if (isContentsDir(zae)) {
                        if (davCmpMap.isEmpty()) {
                            writeOutputStream(true, "PL-BI-1004", zae.getName());
                            isSuccess = false;
                        } else {
                            writeOutputStream(false, "PL-BI-1003", zae.getName());
                        }
                        doneKeys.add(zae.getName());
                        break;
                    }
                }
            } catch (IOException ex) {
                isSuccess = false;
                log.info("IOException: " + ex.getMessage(), ex.fillInStackTrace());
            }

            // 90_contents()???
            if (isSuccess && isContentsDir(zae)) {
                isSuccess = createContents();
            }

            // ??????????
            // ???????
            if (isSuccess) {
                Set<String> filenameList = barFileOrder.keySet();
                for (String filename : filenameList) {
                    Boolean isNecessary = barFileOrder.get(filename);
                    if (isNecessary && !doneKeys.contains(filename)) {
                        String message = PersoniumCoreMessageUtils.getMessage("PL-BI-2001");
                        writeOutputStream(true, "PL-BI-1004", filename, message);
                        isSuccess = false;
                    }
                }
            }
        } catch (Throwable ex) {
            isSuccess = false;
            String message = getErrorMessage(ex);
            log.info("Exception: " + message, ex.fillInStackTrace());
            writeOutputStream(true, "PL-BI-1005", "", message);
        } finally {
            if (isSuccess) {
                writeOutputStream(false, CODE_BAR_INSTALL_COMPLETED, this.cell.getUrl() + boxName, "");
                this.progressInfo.setStatus(ProgressInfo.STATUS.COMPLETED);
            } else {
                String message = PersoniumCoreMessageUtils.getMessage("PL-BI-2001");
                writeOutputStream(false, CODE_BAR_INSTALL_FAILED, this.cell.getUrl() + boxName, message);
                this.progressInfo.setStatus(ProgressInfo.STATUS.FAILED);
            }
            this.progressInfo.setEndTime();
            writeToProgressCache(true);
            IOUtils.closeQuietly(this.zipArchiveInputStream);
            if (this.barFile.exists() && !this.barFile.delete()) {
                log.warn("Failed to remove bar file. [" + this.barFile.getAbsolutePath() + "].");
            }
        }
    }

    /**
     * bar?????.
     */
    private void setEventBus() {
        eventBus = new PersoniumEventBus(this.cell);
        JSONEvent reqBody = new JSONEvent();
        reqBody.setAction(WebDAVMethod.MKCOL.toString());
        reqBody.setLevel(LEVEL.INFO);
        reqBody.setObject(cell.getUrl() + boxName);
        reqBody.setResult("");

        // TODO Box????????
        this.event = EventResource.createEvent(reqBody, this.requestKey, odataEntityResource.getAccessContext());
    }

    /**
     * ???.
     * @param ex 
     * @return 
     */
    private String getErrorMessage(Throwable ex) {
        String message = ex.getMessage();
        // ????????
        if (message == null) {
            message = "throwed " + ex.getClass().getCanonicalName();
        }
        return message;
    }

    /**
     * Zip????"bar/"?????.
     * @return "bar/"????true
     */
    private boolean isRootDir() {
        return isMatchEntryName(ROOT_DIR);
    }

    /**
     * Zip????"bar/00_meta"?????.
     * @return "bar/00_meta"????true
     */
    private boolean isMetadataDir() {
        return isMatchEntryName(META_DIR);
    }

    /**
     * Zip????"bar/90_contents"?????.
     * @param zae ZipArchiveEntry
     * @return "bar/90_contents"????true
     */
    private boolean isContentsDir(ZipArchiveEntry zae) {
        boolean ret = false;
        if (zae == null) {
            ret = isMatchEntryName(CONTENTS_DIR);
        } else {
            ret = zae.getName().equals(CONTENTS_DIR);
        }
        return ret;
    }

    /**
     * Zip????????????????.
     * @param name ?
     * @return ???true
     */
    private boolean isMatchEntryName(String name) {
        boolean ret = false;
        try {
            ZipArchiveEntry zae = this.zipArchiveInputStream.getNextZipEntry();
            if (zae != null) {
                ret = zae.getName().equals(name);
            }
        } catch (IOException ex) {
            log.info("bar file entry was not read.");
            ret = false;
        }
        return ret;
    }

    /**
     * ?BAR?(MB)??
     * @return io.personium.core.bar.entry.maxSize
     */
    private long getMaxBarEntryFileSize() {
        long maxBarFileSize;
        try {
            maxBarFileSize = Long.parseLong(PersoniumUnitConfig.get(PersoniumUnitConfig.BAR.BAR_ENTRY_MAX_SIZE));
        } catch (NumberFormatException ne) {
            log.info("NumberFormatException" + PersoniumUnitConfig.get(PersoniumUnitConfig.BAR.BAR_ENTRY_MAX_SIZE));
            throw PersoniumCoreException.Server.UNKNOWN_ERROR;
        }
        return maxBarFileSize;
    }

    /**
     * bar?1????.
     * @param zae ZipArchiveEntry
     * @param entryName bar??
     * @param maxSize ?(MB)
     * @param keyList 
     * @param doneKeys 
     * @return boolean ????
     */
    protected boolean createMetadata(ZipArchiveEntry zae, String entryName, long maxSize, Set<String> keyList,
            List<String> doneKeys) {
        if (!isValidFileStructure(zae, entryName, maxSize, doneKeys)) {
            return false;
        }

        if (getFileExtension(entryName).equals(".xml")) {
            // XML??
            String boxUrl = this.box.getCell().getUrl() + this.box.getName();
            if (!registXmlEntry(entryName, this.zipArchiveInputStream, boxUrl)) {
                doneKeys.add(entryName);
                return false;
            }
            writeOutputStream(false, "PL-BI-1003", entryName);
            doneKeys.add(entryName);
            return true;
        }

        if (getFileExtension(entryName).equals(".json")) {
            // JSON??
            if (!registJsonEntry(entryName, this.zipArchiveInputStream)) {
                return false;
            }
            writeOutputStream(false, "PL-BI-1003", entryName);
            doneKeys.add(entryName);
            return true;
        }

        if (entryName.endsWith("/")) {
            return true;
        }
        return false;
    }

    /**
     * bar?(bar/90_contents)1????.
     * @return boolean ????
     */
    protected boolean createContents() {
        boolean isSuccess = true;
        // Collection???Map??????
        Map<String, DavCmp> odataCols = getCollections(DavCmp.TYPE_COL_ODATA);
        Map<String, DavCmp> webdavCols = getCollections(DavCmp.TYPE_COL_WEBDAV);
        Map<String, DavCmp> serviceCols = getCollections(DavCmp.TYPE_COL_SVC);

        DavCmp davCmp = null;
        List<String> doneKeys = new ArrayList<String>();
        try {
            ZipArchiveEntry zae = null;
            String currentPath = null;
            int userDataCount = 0;
            List<JSONMappedObject> userDataLinks = new ArrayList<JSONMappedObject>();
            LinkedHashMap<String, BulkRequest> bulkRequests = new LinkedHashMap<String, BulkRequest>();
            Map<String, String> fileNameMap = new HashMap<String, String>();
            PersoniumODataProducer producer = null;

            while ((zae = this.zipArchiveInputStream.getNextZipEntry()) != null) {
                String entryName = zae.getName();
                log.debug("Entry Name: " + entryName);
                log.debug("Entry Size: " + zae.getSize());
                log.debug("Entry Compressed Size: " + zae.getCompressedSize());
                if (!zae.isDirectory()) {
                    this.progressInfo.addDelta(1L);
                }
                writeOutputStream(false, CODE_BAR_INSTALL_STARTED, entryName);

                // ODataCollection?Dav/ServiceCollection/ODataCollection?????????
                // ??????????
                if (currentPath != null && !entryName.startsWith(currentPath)) {
                    if (!execBulkRequest(davCmp.getCell().getId(), bulkRequests, fileNameMap, producer)) {
                        return false;
                    }
                    if (!createUserdataLinks(producer, userDataLinks)) {
                        return false;
                    }
                    userDataLinks = new ArrayList<JSONMappedObject>();
                    currentPath = null;
                }
                int entryType = getEntryType(entryName, odataCols, webdavCols, serviceCols, this.davFileMap);
                switch (entryType) {
                case TYPE_ODATA_COLLECTION:
                    // OData?
                    if (!odataCols.isEmpty()) {
                        if (!isValidODataContents(entryName, odataCols, doneKeys)) {
                            return false;
                        }
                        Pattern formatPattern = Pattern.compile(CONTENTS_DIR + ".+/90_data/");
                        Matcher formatMatcher = formatPattern.matcher(entryName);
                        if (formatMatcher.matches()) {
                            currentPath = entryName;
                        }
                        Pattern userodataDirPattern = Pattern.compile(CONTENTS_DIR + ".+/90_data/.+");
                        Matcher userodataDirMatcher = userodataDirPattern.matcher(entryName);

                        if (getFileExtension(entryName).equals(".xml")) {
                            // 00_$metadata.xml??
                            davCmp = getCollection(entryName, odataCols);
                            // OData????????
                            producer = davCmp.getODataProducer();
                            if (!registUserSchema(entryName, this.zipArchiveInputStream, davCmp)) {
                                doneKeys.add(entryName);
                                return false;
                            }
                            writeOutputStream(false, "PL-BI-1003", entryName);
                            doneKeys.add(entryName);
                            continue;
                        } else if (entryName.endsWith(USERDATA_LINKS_JSON)) {
                            userDataLinks = registJsonLinksUserdata(entryName, this.zipArchiveInputStream);
                            if (userDataLinks == null) {
                                doneKeys.add(entryName);
                                return false;
                            }
                            writeOutputStream(false, "PL-BI-1003", entryName);
                            doneKeys.add(entryName);
                            continue;
                        } else if (userodataDirMatcher.matches() && getFileExtension(entryName).equals(".json")) {
                            userDataCount++;
                            if (!setBulkRequests(entryName, producer, bulkRequests, fileNameMap)) {
                                return false;
                            }
                            doneKeys.add(entryName);

                            if ((userDataCount % bulkSize) == 0 && !execBulkRequest(davCmp.getCell().getId(),
                                    bulkRequests, fileNameMap, producer)) {
                                return false;
                            }
                            continue;
                        } else if (!entryName.endsWith("/")) {
                            // xml,json?????????
                            String message = PersoniumCoreMessageUtils.getMessage("PL-BI-2001");
                            log.info(message + " [" + entryName + "]");
                            writeOutputStream(true, "PL-BI-1004", entryName, message);
                            return false;
                        }
                    }
                    break;

                case TYPE_DAV_FILE:
                    // WebDAV?
                    // bar/90_contents/{davcol_name}??1????
                    if (!registWebDavFile(entryName, this.zipArchiveInputStream, webdavCols)) {
                        return false;
                    }
                    break;

                case TYPE_SVC_FILE:
                    // Service?
                    if (!installSvcCollection(webdavCols, entryName)) {
                        return false;
                    }
                    break;

                case TYPE_MISMATCH:
                    // OData??????????rootprops??????
                    String message = PersoniumCoreMessageUtils.getMessage("PL-BI-2006");
                    log.info(message + " [" + entryName + "]");
                    writeOutputStream(true, "PL-BI-1004", entryName, message);
                    return false;

                default:
                    break;
                }
                writeOutputStream(false, "PL-BI-1003", entryName);
                doneKeys.add(entryName);
            }

            // ODataCollection??????????????????
            if (currentPath != null) {
                if (!execBulkRequest(davCmp.getCell().getId(), bulkRequests, fileNameMap, producer)) {
                    return false;
                }
                if (!createUserdataLinks(producer, userDataLinks)) {
                    return false;
                }
                userDataLinks = null;
            }
        } catch (IOException ex) {
            isSuccess = false;
            log.info("IOException: " + ex.getMessage(), ex.fillInStackTrace());
            String message = PersoniumCoreMessageUtils.getMessage("PL-BI-2000");
            writeOutputStream(true, CODE_BAR_INSTALL_FAILED, "", message);

        }
        // bar/90_contents/{odatacol_name}/00_$metadata.xml)??
        isSuccess = checkNecessaryFile(isSuccess, odataCols, doneKeys);
        return isSuccess;
    }

    private boolean checkNecessaryFile(boolean isSuccess, Map<String, DavCmp> odataCols, List<String> doneKeys) {
        Set<String> colList = odataCols.keySet();
        for (String colName : colList) {
            String filename = colName + METADATA_XML;
            if (!doneKeys.contains(filename)) {
                String message = PersoniumCoreMessageUtils.getMessage("PL-BI-2001");
                writeOutputStream(true, "PL-BI-1004", filename, message);
                isSuccess = false;
            }
        }
        return isSuccess;
    }

    private boolean installSvcCollection(Map<String, DavCmp> webdavCols, String entryName) {
        // bar/90_contents/{svccol_name}??1???WebDAV/????
        // {serviceCollection}/{scriptName}{serviceCollection}/__src/{scriptName}??
        int lastSlashIndex = entryName.lastIndexOf("/");
        StringBuilder serviceSrcName = new StringBuilder();
        serviceSrcName.append(entryName.substring(0, lastSlashIndex));
        serviceSrcName.append("/__src");
        serviceSrcName.append(entryName.substring(lastSlashIndex));

        if (!registWebDavFile(serviceSrcName.toString(), this.zipArchiveInputStream, webdavCols)) {
            return false;
        }
        return true;
    }

    private boolean setBulkRequests(String entryName, PersoniumODataProducer producer,
            LinkedHashMap<String, BulkRequest> bulkRequests, Map<String, String> fileNameMap) {
        BulkRequest bulkRequest = new BulkRequest();
        String key = PersoniumUUID.randomUUID();
        try {
            // entityType????
            String entityTypeName = getEntityTypeName(entryName);
            if (producer.getMetadata().findEdmEntitySet(entityTypeName) == null) {
                throw PersoniumCoreException.OData.NO_SUCH_ENTITY_SET;
            }

            // ZipArchiveImputStream??JSONStringReader????
            StringReader stringReader = getStringReaderFromZais();

            // ??
            ODataResource odataResource = odataEntityResource.getOdataResource();
            ODataEntitiesResource resource = new ODataEntitiesResource(odataResource, entityTypeName);
            OEntity oEntity = resource.getOEntityWrapper(stringReader, odataResource, producer.getMetadata());

            UserDataODataProducer userDataProducer = (UserDataODataProducer) producer;
            EntitySetDocHandler docHandler = producer.getEntitySetDocHandler(entityTypeName, oEntity);
            String docType = UserDataODataProducer.USER_ODATA_NAMESPACE;
            docHandler.setType(docType);
            docHandler.setEntityTypeId(userDataProducer.getEntityTypeId(oEntity.getEntitySetName()));

            odataEntityResource.setOdataProducer(userDataProducer);

            // ??ID??
            // TODO ?????NTKP
            key = oEntity.getEntitySetName() + ":" + (String) docHandler.getStaticFields().get("__id");

            if (bulkRequests.containsKey(key)) {
                throw PersoniumCoreException.OData.ENTITY_ALREADY_EXISTS;
            }

            // ID?????UUID??
            if (docHandler.getId() == null) {
                docHandler.setId(PersoniumUUID.randomUUID());
            }
            bulkRequest.setEntitySetName(entityTypeName);
            bulkRequest.setDocHandler(docHandler);
        } catch (Exception e) {
            writeOutputStream(true, "PL-BI-1004", entryName, e.getMessage());
            log.info(entryName + " : " + e.getMessage());
            bulkRequest.setError(e);
            return false;
        }
        bulkRequests.put(key, bulkRequest);
        fileNameMap.put(key, entryName);
        return true;
    }

    private StringReader getStringReaderFromZais() throws IOException {
        BufferedReader bufferedReader = new BufferedReader(
                new InputStreamReader(this.zipArchiveInputStream, "UTF-8"));
        StringBuffer buf = new StringBuffer();
        String str = null;
        while ((str = bufferedReader.readLine()) != null) {
            buf.append(str);
        }
        StringReader stringReader = new StringReader(buf.toString());
        return stringReader;
    }

    private String getEntityTypeName(String entryName) {
        String[] hierarchy = entryName.split("/");
        int size = hierarchy.length;
        String entityTypeName = hierarchy[size - 2];
        return entityTypeName;
    }

    private boolean execBulkRequest(String cellId, LinkedHashMap<String, BulkRequest> bulkRequests,
            Map<String, String> fileNameMap, PersoniumODataProducer producer) {
        // ??
        producer.bulkCreateEntity(producer.getMetadata(), bulkRequests, cellId);

        // ???
        for (Entry<String, BulkRequest> request : bulkRequests.entrySet()) {
            // ???????????
            if (request.getValue().getError() != null) {
                if (request.getValue().getError() instanceof PersoniumCoreException) {
                    PersoniumCoreException e = ((PersoniumCoreException) request.getValue().getError());
                    writeOutputStream(true, "PL-BI-1004", fileNameMap.get(request.getKey()), e.getMessage());
                    log.info("PersoniumCoreException: " + e.getMessage());
                } else {
                    Exception e = request.getValue().getError();
                    String message = PersoniumCoreMessageUtils.getMessage("PL-BI-2003");
                    writeOutputStream(true, "PL-BI-1004", fileNameMap.get(request.getKey()), message);
                    log.info("Regist Entity Error: " + e.toString());
                    log.info("Regist Entity Error: " + e.getClass().getName());
                    log.info("Regist Entity Error: " + e);
                }
                return false;
            }
            writeOutputStream(false, "PL-BI-1003", fileNameMap.get(request.getKey()));
        }

        bulkRequests.clear();
        fileNameMap.clear();
        return true;
    }

    /**
     * bar?90_contents?????.
     * @param entryName bar???
     * @param odataCols OData?
     * @param webdavCols WebDAV?
     * @param serviceCols ?
     * @param davFiles WebDAV?
     * @return ?
     */
    protected int getEntryType(String entryName, Map<String, DavCmp> odataCols, Map<String, DavCmp> webdavCols,
            Map<String, DavCmp> serviceCols, Map<String, String> davFiles) {

        if (odataCols.containsKey(entryName)) {
            return TYPE_ODATA_COLLECTION;
        } else if (webdavCols.containsKey(entryName)) {
            return TYPE_WEBDAV_COLLECTION;
        } else if (serviceCols.containsKey(entryName)) {
            return TYPE_SERVICE_COLLECTION;
        } else if (davFiles.containsKey(entryName)) {
            return TYPE_DAV_FILE;
        }

        for (Entry<String, DavCmp> entry : odataCols.entrySet()) {
            String odataColPath = entry.getKey();
            if (entryName.startsWith(odataColPath)) {
                return TYPE_ODATA_COLLECTION;
            }
        }

        for (Entry<String, DavCmp> entry : serviceCols.entrySet()) {
            String serviceColPath = entry.getKey();
            if (entryName.startsWith(serviceColPath)) {
                return TYPE_SVC_FILE;
            }
        }

        return TYPE_MISMATCH;
    }

    /**
     * WebDAV??.
     * @param entryName bar???
     * @param inputStream 
     * @param webdavCols WebDAV
     * @return true: ??false:
     */
    protected boolean registWebDavFile(String entryName, InputStream inputStream, Map<String, DavCmp> webdavCols) {

        // ????
        String filePath = entryName.replaceAll(CONTENTS_DIR, "");
        String colPath = entryName.substring(0, entryName.lastIndexOf("/") + 1);

        // DavCmp?
        DavCmp parentCmp = webdavCols.get(colPath);

        // ???
        int maxChildResource = PersoniumUnitConfig.getMaxChildResourceCount();
        if (parentCmp.getChildrenCount() >= maxChildResource) {
            // ?????????????
            String message = PersoniumCoreMessageUtils.getMessage("PR400-DV-0007");
            log.info(message);
            writeOutputStream(true, "PL-BI-1004", entryName, message);
            return false;
        }

        // ???

        // ??
        String fileName = "";
        fileName = filePath.substring(filePath.lastIndexOf("/") + 1);

        // ?
        DavCmp fileCmp = parentCmp.getChild(fileName);

        // Content-Type??
        String contentType = null;
        try {
            contentType = this.davFileMap.get(entryName);
            RuntimeDelegate.getInstance().createHeaderDelegate(MediaType.class).fromString(contentType);
        } catch (Exception e) {
            String message = PersoniumCoreMessageUtils.getMessage("PL-BI-2005");
            log.info(message + ": " + e.getMessage(), e.fillInStackTrace());
            writeOutputStream(true, "PL-BI-1004", entryName, message);
            return false;
        }

        // 
        try {
            fileCmp.putForCreate(contentType, new CloseShieldInputStream(inputStream));
        } catch (Exception e) {
            String message = PersoniumCoreMessageUtils.getMessage("PL-BI-2004");
            log.info(message + ": " + e.getMessage(), e.fillInStackTrace());
            writeOutputStream(true, "PL-BI-1004", entryName, message);
            return false;
        }

        return true;
    }

    /**
     * ?????.
     * @param filename ??
     */
    private String getFileExtension(String filename) {
        String extension = "";
        int idx = filename.lastIndexOf(".");
        if (idx >= 0) {
            extension = filename.substring(idx);
        }
        return extension;
    }

    /**
     * 90_rootprops_xml???Collectoin/ACL/WebDAV???.
     * @param rootPropsName 90_rootprops_xml?bar??
     * @param inputStream 
     * @param boxUrl box?URL
     * @return ????true
     */
    protected boolean registXmlEntry(String rootPropsName, InputStream inputStream, String boxUrl) {
        // XML(StAX,SAX,DOM)?InputStream???????????
        // ?????????????????
        try {
            BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream, "UTF-8"));
            StringBuffer buf = new StringBuffer();
            String str;
            while ((str = bufferedReader.readLine()) != null) {
                buf.append(str);
            }

            Multistatus multiStatus = Multistatus.unmarshal(new ByteArrayInputStream(buf.toString().getBytes()));

            // 90_rootprops.xml??????
            // ?????????????
            if (!validateCollectionDefinitions(multiStatus, rootPropsName)) {
                return false;
            }
            for (Response response : multiStatus.getResponse()) {
                int collectionType = TYPE_WEBDAV_COLLECTION;
                boolean hasCollection = false;
                boolean isBox = false;

                List<String> hrefs = response.getHref();
                String href = hrefs.get(0);
                if (href.equals("dcbox:")) {
                    href = DCBOX;
                }
                if (href.equals(DCBOX)) {
                    isBox = true;
                }
                String collectionUrl = null;
                collectionUrl = href.replaceFirst(DCBOX, boxUrl + "/");

                List<Element> propElements = new ArrayList<Element>();
                Element aclElement = null;
                String contentType = null;
                for (Propstat propstat : response.getPropstat()) {
                    Prop prop = propstat.getProp();
                    Resourcetype resourceType = prop.getResourcetype();
                    if (resourceType != null) {
                        if (resourceType.getCollection() != null) {
                            hasCollection = true;
                        }
                        List<Element> elements = resourceType.getAny();
                        for (Element element : elements) {
                            String nodeName = element.getNodeName();
                            if (nodeName.equals("p:odata")) {
                                collectionType = TYPE_ODATA_COLLECTION;
                            } else if (nodeName.equals("p:service")) {
                                collectionType = TYPE_SERVICE_COLLECTION;
                            }
                        }
                    }
                    // prop??
                    Getcontenttype getContentType = prop.getGetcontenttype();
                    if (getContentType != null) {
                        contentType = getContentType.getValue();
                    }

                    List<Element> pElements = prop.getAny();
                    for (Element element : pElements) {
                        String nodeName = element.getNodeName();
                        if (nodeName.equals("creationdate") || nodeName.equals("getlastmodified")
                                || nodeName.equals("resourcetype")) {
                            continue;
                        }
                        if (nodeName.equals("acl")) {
                            if (!BarFileUtils.aclNameSpaceValidate(rootPropsName, element, this.schemaUrl)) {
                                String message = PersoniumCoreMessageUtils.getMessage("PL-BI-2007");
                                log.info(message + " [" + rootPropsName + "]");
                                writeOutputStream(true, "PL-BI-1004", rootPropsName, message);
                                return false;
                            }
                            aclElement = element;
                            continue;
                        }
                        propElements.add(element);
                    }
                }

                String entryName = CONTENTS_DIR + href.replaceFirst(DCBOX, "");
                if (isBox) {
                    // Box???ACL
                    registBoxAclAndProppatch(this.box, aclElement, propElements, collectionUrl);
                } else if (hasCollection) {
                    if (!entryName.endsWith("/")) {
                        entryName += "/";
                    }
                    // ????ACL?PROPPATH
                    log.info(entryName);
                    createCollection(collectionUrl, entryName, this.cell, this.box, collectionType, aclElement,
                            propElements);
                } else {
                    // WebDAV
                    this.davFileMap.put(entryName, contentType);
                }
            }
        } catch (PersoniumCoreException e) {
            log.info("PersoniumCoreException: " + e.getMessage());
            writeOutputStream(true, "PL-BI-1004", rootPropsName, e.getMessage());
            return false;
        } catch (Exception ex) {
            String message = getErrorMessage(ex);
            log.info("XMLParseException: " + message, ex.fillInStackTrace());
            writeOutputStream(true, "PL-BI-1004", rootPropsName, message);
            return false;
        }
        return true;
    }

    /**
     * 90_rootprops.xml???path????????.
     * @param multiStatus 90_rootprops.xml???JAXB
     * @param rootPropsName ?????()
     * @return ?????true?????false?
     */
    protected boolean validateCollectionDefinitions(Multistatus multiStatus, String rootPropsName) {

        // XML???href???OData/WebDAV/?WebDAV?)??
        Map<String, Integer> pathMap = new LinkedHashMap<String, Integer>();
        for (Response response : multiStatus.getResponse()) {
            List<String> hrefs = response.getHref();
            if (hrefs.size() != 1) {
                String message = PersoniumCoreMessageUtils.getMessage("PL-BI-2008");
                writeOutputStream(true, "PL-BI-1004", rootPropsName, message);
                return false;
            }
            String href = hrefs.get(0);
            // href?????????
            if (href == null || href.length() == 0) {
                String message = PersoniumCoreMessageUtils.getMessage("PL-BI-2009");
                writeOutputStream(true, "PL-BI-1004", rootPropsName, message);
                return false;
            }
            // href???"dcbox:/" ??????????
            if (!href.startsWith(DCBOX_NO_SLUSH)) {
                String message = MessageFormat.format(PersoniumCoreMessageUtils.getMessage("PL-BI-2010"), href);
                writeOutputStream(true, "PL-BI-1004", rootPropsName, message);
                return false;
            }
            // ????????????????
            int collectionType = getCollectionType(rootPropsName, response);
            switch (collectionType) {
            case TYPE_WEBDAV_COLLECTION:
            case TYPE_ODATA_COLLECTION:
            case TYPE_SERVICE_COLLECTION:
                if (href.endsWith("/")) {
                    href = href.substring(0, href.length() - 1);
                }
                break;
            case TYPE_MISMATCH:
                return false;
            default:
                break;
            }
            // ???????????????????
            // ?"/"???????????
            if (pathMap.containsKey(href)) {
                String message = MessageFormat.format(PersoniumCoreMessageUtils.getMessage("PL-BI-2011"), href);
                writeOutputStream(true, "PL-BI-1004", rootPropsName, message);
                return false;
            }
            pathMap.put(href, Integer.valueOf(collectionType));
        }
        // ????Collection??
        // Box????
        //  ??????
        // OData?? ????????
        // Service?? ?? "__src" ?????
        Set<String> keySet = pathMap.keySet();
        for (Entry<String, Integer> entry : pathMap.entrySet()) {
            String href = entry.getKey();
            int currentCollectionType = entry.getValue();
            int upperPathposition = href.lastIndexOf("/");
            if (upperPathposition < 0) { // "dcbox:"???????
                continue;
            }
            // ????????????????
            // Box???????????
            String upper = href.substring(0, upperPathposition);
            if (!keySet.contains(upper)) {
                String message = MessageFormat.format(PersoniumCoreMessageUtils.getMessage("PL-BI-2012"), upper);
                writeOutputStream(true, "PL-BI-1004", rootPropsName, message);
                return false;
            }
            int upperCollectionType = pathMap.get(upper);
            String resourceName = href.substring(upperPathposition + 1, href.length());
            if (upperCollectionType == TYPE_ODATA_COLLECTION) {
                // OData????????????
                String message = MessageFormat.format(PersoniumCoreMessageUtils.getMessage("PL-BI-2013"), href);
                writeOutputStream(true, "PL-BI-1004", rootPropsName, message);
                return false;
            } else if (upperCollectionType == TYPE_SERVICE_COLLECTION) {
                // Service????????????
                // ????"__src"???????
                if (!("__src".equals(resourceName) && currentCollectionType == TYPE_WEBDAV_COLLECTION)) {
                    String message = MessageFormat.format(PersoniumCoreMessageUtils.getMessage("PL-BI-2014"), href);
                    writeOutputStream(true, "PL-BI-1004", rootPropsName, message);
                    return false;
                }
            } else if (upperCollectionType == TYPE_DAV_FILE) {
                // WebDAV?Service????????????
                String message = MessageFormat.format(PersoniumCoreMessageUtils.getMessage("PL-BI-2015"), href);
                writeOutputStream(true, "PL-BI-1004", rootPropsName, message);
                return false;
            }
            // ?Service?????"__src"??????????
            if (currentCollectionType == TYPE_SERVICE_COLLECTION) {
                String srcPath = href + "/__src";
                if (!keySet.contains(srcPath) || pathMap.get(srcPath) != TYPE_WEBDAV_COLLECTION) {
                    String message = MessageFormat.format(PersoniumCoreMessageUtils.getMessage("PL-BI-2016"), href);
                    writeOutputStream(true, "PL-BI-1004", rootPropsName, message);
                    return false;
                }
            }

            // ?????????????????
            if (!DavCommon.isValidResourceName(resourceName)) {
                String message = MessageFormat.format(PersoniumCoreMessageUtils.getMessage("PL-BI-2017"),
                        resourceName);
                writeOutputStream(true, "PL-BI-1004", rootPropsName, message);
                return false;
            }
        }
        return true;
    }

    /**
     * 90_rootprops.xml??response???????
     * @param rootPropsName ?????()
     * @param response ??responseJAXB
     * @return ?????
     *         WebDAV?Service?WebDAV????
     *         ??????????????????
     */
    private int getCollectionType(String rootPropsName, Response response) {
        // <propstat>???????????
        // ?prop/resourcetype/collecton ?DOM????????
        // ???"p:odata" ??? "p:service" ?DOM??????WebDAV????
        // - ????????WebDAv???????
        for (Propstat propstat : response.getPropstat()) {
            Prop prop = propstat.getProp();
            Resourcetype resourceType = prop.getResourcetype();
            if (resourceType != null && resourceType.getCollection() != null) {
                List<Element> elements = resourceType.getAny();
                for (Element element : elements) {
                    String nodeName = element.getNodeName();
                    if (nodeName.equals("p:odata")) {
                        return TYPE_ODATA_COLLECTION;
                    } else if (nodeName.equals("p:service")) {
                        return TYPE_SERVICE_COLLECTION;
                    } else {
                        String message = MessageFormat.format(PersoniumCoreMessageUtils.getMessage("PL-BI-2018"),
                                nodeName);
                        writeOutputStream(true, "PL-BI-1004", rootPropsName, message);
                        return TYPE_MISMATCH;
                    }
                }
            } else {
                return TYPE_DAV_FILE;
            }
        }
        return TYPE_WEBDAV_COLLECTION;
    }

    /**
     * JSON???.
     * @param entryName ??
     * @param inputStream 
     * @return ????true
     */
    private boolean registJsonEntry(String entryName, InputStream inputStream) {
        JsonParser jp = null;
        ObjectMapper mapper = new ObjectMapper();
        JsonFactory f = new JsonFactory();
        try {
            jp = f.createJsonParser(inputStream);
            JsonToken token = jp.nextToken(); // JSON?"{"
            Pattern formatPattern = Pattern.compile(".*/+(.*)");
            Matcher formatMatcher = formatPattern.matcher(entryName);
            String jsonName = formatMatcher.replaceAll("$1");

            if (token == JsonToken.START_OBJECT) {
                if (jsonName.equals(RELATION_JSON) || jsonName.equals(ROLE_JSON) || jsonName.equals(EXTROLE_JSON)
                        || jsonName.equals(LINKS_JSON)) {
                    registJsonEntityData(jp, mapper, jsonName);
                } else if (jsonName.equals(MANIFEST_JSON)) {
                    manifestJsonValidate(jp, mapper); // Box?????
                }
                log.debug(jsonName);
            } else {
                throw PersoniumCoreException.BarInstall.JSON_FILE_FORMAT_ERROR.params(jsonName);
            }
        } catch (PersoniumCoreException e) {
            // JSON??
            writeOutputStream(true, "PL-BI-1004", entryName, e.getMessage());
            log.info("PersoniumCoreException" + e.getMessage(), e.fillInStackTrace());
            return false;
        } catch (JsonParseException e) {
            // JSON??
            String message = PersoniumCoreMessageUtils.getMessage("PL-BI-2002");
            writeOutputStream(true, "PL-BI-1004", entryName, message);
            log.info("JsonParseException: " + e.getMessage(), e.fillInStackTrace());
            return false;
        } catch (JsonMappingException e) {
            // JSON?
            String message = PersoniumCoreMessageUtils.getMessage("PL-BI-2003");
            writeOutputStream(true, "PL-BI-1004", entryName, message);
            log.info("JsonMappingException: " + e.getMessage(), e.fillInStackTrace());
            return false;
        } catch (Exception e) {
            String message = PersoniumCoreMessageUtils.getMessage("PL-BI-2000");
            writeOutputStream(true, "PL-BI-1004", entryName, message);
            log.info("Exception: " + e.getMessage(), e.fillInStackTrace());
            return false;
        }
        return true;
    }

    /**
     * 10_odatarelations.json????Link??.
     * @param entryName ??
     * @param inputStream 
     * @return ????true
     */
    protected List<JSONMappedObject> registJsonLinksUserdata(String entryName, InputStream inputStream) {
        List<JSONMappedObject> userDataLinks = new ArrayList<JSONMappedObject>();
        JsonParser jp = null;
        ObjectMapper mapper = new ObjectMapper();
        JsonFactory f = new JsonFactory();
        try {
            jp = f.createJsonParser(inputStream);
            JsonToken token = jp.nextToken(); // JSON?"{"

            if (token == JsonToken.START_OBJECT) {
                token = jp.nextToken();

                // $links??
                checkMatchFieldName(jp, USERDATA_LINKS_JSON);

                token = jp.nextToken();
                // ?????
                if (token != JsonToken.START_ARRAY) {
                    throw PersoniumCoreException.BarInstall.JSON_FILE_FORMAT_ERROR.params(USERDATA_LINKS_JSON);
                }
                token = jp.nextToken();

                while (jp.hasCurrentToken()) {
                    if (token == JsonToken.END_ARRAY) {
                        break;
                    } else if (token != JsonToken.START_OBJECT) {
                        throw PersoniumCoreException.BarInstall.JSON_FILE_FORMAT_ERROR.params(USERDATA_LINKS_JSON);
                    }
                    userDataLinks.add(barFileJsonValidate(jp, mapper, USERDATA_LINKS_JSON));

                    token = jp.nextToken();
                }
            } else {
                throw PersoniumCoreException.BarInstall.JSON_FILE_FORMAT_ERROR.params(USERDATA_LINKS_JSON);
            }
        } catch (JsonParseException e) {
            // JSON??
            String message = PersoniumCoreMessageUtils.getMessage("PL-BI-2002");
            writeOutputStream(true, "PL-BI-1004", entryName, message);
            log.info("JsonParseException: " + e.getMessage(), e.fillInStackTrace());
            return null;
        } catch (JsonMappingException e) {
            // JSON?
            String message = PersoniumCoreMessageUtils.getMessage("PL-BI-2003");
            writeOutputStream(true, "PL-BI-1004", entryName, message);
            log.info("JsonMappingException: " + e.getMessage(), e.fillInStackTrace());
            return null;
        } catch (PersoniumCoreException e) {
            // JSON??
            writeOutputStream(true, "PL-BI-1004", entryName, e.getMessage());
            log.info("PersoniumCoreException" + e.getMessage(), e.fillInStackTrace());
            return null;
        } catch (IOException e) {
            String message = PersoniumCoreMessageUtils.getMessage("PL-BI-2000");
            writeOutputStream(true, "PL-BI-1004", entryName, message);
            log.info("IOException: " + e.getMessage(), e.fillInStackTrace());
            return null;
        }
        return userDataLinks;
    }

    /**
     * ????????.
     * @param zae
     * @param entryName
     * @param maxSize
     * @param doneKeys
     * @return ???true?
     */
    private boolean isValidFileStructure(ZipArchiveEntry zae, String entryName, long maxSize,
            List<String> doneKeys) {
        writeOutputStream(false, CODE_BAR_INSTALL_STARTED, entryName);

        // ???????
        if (!barFileOrder.containsKey(entryName)) {
            log.info("[" + entryName + "] invalid file");
            String message = PersoniumCoreMessageUtils.getMessage("PL-BI-2001");
            writeOutputStream(true, "PL-BI-1004", entryName, message);
            return false;
        }

        // ?????
        Pattern formatPattern = Pattern.compile(".*/+([0-9][0-9])_.*");
        Matcher formatMatcher = formatPattern.matcher(entryName);
        String entryIndex = formatMatcher.replaceAll("$1");
        if (doneKeys.isEmpty()) {
            // ?????"00"?????
            if (!entryIndex.equals("00")) {
                log.info("bar/00_meta/00_manifest.json is not exsist");
                String message = PersoniumCoreMessageUtils.getMessage("PL-BI-2001");
                writeOutputStream(true, "PL-BI-1004", entryName, message);
                return false;
            }
        } else {
            String lastEntryName = doneKeys.get(doneKeys.size() - 1);
            formatMatcher = formatPattern.matcher(lastEntryName);
            String lastEntryIndex = formatMatcher.replaceAll("$1");

            // ??????
            if (entryIndex.compareTo(lastEntryIndex) < 0) {
                log.info("[" + entryName + "] invalid file");
                String message = PersoniumCoreMessageUtils.getMessage("PL-BI-2001");
                writeOutputStream(true, "PL-BI-1004", entryName, message);
                return false;
            }
        }

        // [400]bar/bar??????
        if (zae.getSize() > (long) (maxSize * MB)) {
            log.info("Bar file entry size too large invalid file [" + entryName + "]");
            String message = PersoniumCoreException.BarInstall.BAR_FILE_ENTRY_SIZE_TOO_LARGE
                    .params(zae.getName(), String.valueOf(zae.getSize())).getMessage();
            writeOutputStream(true, "PL-BI-1004", entryName, message);
            return false;
        }
        return true;
    }

    /**
     * bar/90_contents/{OdataCol_name}?????????????.
     * @param entryName ??(??)
     * @param colMap ?Map
     * @param doneKeys ???OData
     * @return ??
     */
    protected boolean isValidODataContents(String entryName, Map<String, DavCmp> colMap, List<String> doneKeys) {

        String odataColPath = "";
        for (Map.Entry<String, DavCmp> entry : colMap.entrySet()) {
            if (entryName.startsWith(entry.getKey())) {
                odataColPath = entry.getKey();
                break;
            }
        }

        // OData????
        String odataPath = entryName.replaceAll(odataColPath, "");

        // bar/90_contents/{OData_collection}???
        if (USERDATA_LINKS_JSON.equals(odataPath)) {

            // 00_$metadata.xml????????
            String meatadataPath = odataColPath + METADATA_XML;
            if (!doneKeys.contains(meatadataPath)) {
                String message = PersoniumCoreMessageUtils.getMessage("PL-BI-2001");
                log.info(message + "entryName: " + entryName);
                writeOutputStream(true, "PL-BI-1004", entryName, message);
                return false;
            }
            // 90_data/??????????
            String userDataPath = odataColPath + USERDATA_DIR_NAME + "/";
            if (doneKeys.contains(userDataPath)) {
                String message = PersoniumCoreMessageUtils.getMessage("PL-BI-2001");
                log.info(message + "entryName: " + entryName);
                writeOutputStream(true, "PL-BI-1004", entryName, message);
                return false;
            }
        }
        if (odataPath.startsWith(USERDATA_DIR_NAME + "/")) {
            // 00_$metadata.xml????????
            String meatadataPath = odataColPath + METADATA_XML;
            if (!doneKeys.contains(meatadataPath)) {
                String message = PersoniumCoreMessageUtils.getMessage("PL-BI-2001");
                log.info(message + "entryName: " + entryName);
                writeOutputStream(true, "PL-BI-1004", entryName, message);
                return false;
            }
        }

        // bar/90_contents/{OData_collection}/{dirPath}/??
        String dirPath = null;
        Pattern pattern = Pattern.compile("^([^/]+)/.*");
        Matcher m = pattern.matcher(odataPath);
        if (m.matches()) {
            dirPath = m.replaceAll("$1");
        }
        if (dirPath != null && !dirPath.equals(USERDATA_DIR_NAME)) {
            // bar/90_contents/{OData_collection}/{dir}/?????
            String message = PersoniumCoreMessageUtils.getMessage("PL-BI-2001");
            log.info(message + "entryName: " + entryName);
            writeOutputStream(true, "PL-BI-1004", entryName, message);
            return false;
        }

        // bar/90_contents/{OData_collection}/90_data/{entity}/{1.json}??
        String fileName = null;
        pattern = Pattern.compile(".*/([^/]+)$");
        m = pattern.matcher(odataPath);
        if (m.matches()) {
            fileName = m.replaceAll("$1");
        }
        if (fileName != null) {
            pattern = Pattern.compile("^([0-9]+).json$");
            m = pattern.matcher(fileName);
            if (!m.matches()) {
                // bar/90_contents/{OData_collection}/{dir}/?????
                String message = PersoniumCoreMessageUtils.getMessage("PL-BI-2001");
                log.info(message + "entryName: " + entryName);
                writeOutputStream(true, "PL-BI-1004", entryName, message);
                return false;
            }
        }

        return true;
    }

    /**
     * 10_relations.json, 20_roles.json, 30_extroles.json, 70_$links.json, 10_odatarelations.json???.
     * @param jp Json
     * @param mapper ObjectMapper
     * @param jsonName ??
     * @throws IOException IOException
     */
    protected void registJsonEntityData(JsonParser jp, ObjectMapper mapper, String jsonName) throws IOException {
        JsonToken token;
        token = jp.nextToken();

        // Relations,Roles,ExtRoles,$links??
        checkMatchFieldName(jp, jsonName);

        token = jp.nextToken();
        // ?????
        if (token != JsonToken.START_ARRAY) {
            throw PersoniumCoreException.BarInstall.JSON_FILE_FORMAT_ERROR.params(jsonName);
        }
        token = jp.nextToken();

        while (jp.hasCurrentToken()) {
            if (token == JsonToken.END_ARRAY) {
                break;
            } else if (token != JsonToken.START_OBJECT) {
                throw PersoniumCoreException.BarInstall.JSON_FILE_FORMAT_ERROR.params(jsonName);
            }

            // 1?
            JSONMappedObject mappedObject = barFileJsonValidate(jp, mapper, jsonName);
            if (jsonName.equals(RELATION_JSON)) {
                createRelation(mappedObject.getJson());
            } else if (jsonName.equals(ROLE_JSON)) {
                createRole(mappedObject.getJson());
            } else if (jsonName.equals(EXTROLE_JSON)) {
                createExtRole(mappedObject.getJson());
            } else if (jsonName.equals(LINKS_JSON)) {
                createLinks(mappedObject, odataProducer);
            }

            token = jp.nextToken();
        }
    }

    /**
     * ???.
     * @param jp Json
     * @param mapper ObjectMapper
     * @param jsonName ??
     * @throws IOException IOException
     * @return JSONMappedObject JSONMapped
     */
    protected JSONMappedObject barFileJsonValidate(JsonParser jp, ObjectMapper mapper, String jsonName)
            throws IOException {
        if (jsonName.equals(EXTROLE_JSON)) {
            JSONExtRoles extRoles = mapper.readValue(jp, JSONExtRoles.class);
            if (extRoles.getExtRole() == null) {
                throw PersoniumCoreException.BarInstall.JSON_FILE_FORMAT_ERROR.params(jsonName);
            }
            if (extRoles.getRelationName() == null) {
                throw PersoniumCoreException.BarInstall.JSON_FILE_FORMAT_ERROR.params(jsonName);
            }
            return extRoles;
        } else if (jsonName.equals(ROLE_JSON)) {
            JSONRoles roles = mapper.readValue(jp, JSONRoles.class);
            if (roles.getName() == null) {
                throw PersoniumCoreException.BarInstall.JSON_FILE_FORMAT_ERROR.params(jsonName);
            }
            return roles;
        } else if (jsonName.equals(RELATION_JSON)) {
            JSONRelations relations = mapper.readValue(jp, JSONRelations.class);
            if (relations.getName() == null) {
                throw PersoniumCoreException.BarInstall.JSON_FILE_FORMAT_ERROR.params(jsonName);
            }
            return relations;
        } else if (jsonName.equals(LINKS_JSON)) {
            JSONLinks links = mapper.readValue(jp, JSONLinks.class);
            linksJsonValidate(jsonName, links);
            return links;
        } else if (jsonName.equals(USERDATA_LINKS_JSON)) {
            JSONUserDataLinks links = mapper.readValue(jp, JSONUserDataLinks.class);
            userDataLinksJsonValidate(jsonName, links);
            return links;
        }
        return null;
    }

    /**
     * 70_$links.json??.
     * @param jsonName JSON??
     * @param links ??JSON
     */
    private void linksJsonValidate(String jsonName, JSONLinks links) {
        if (links.getFromType() == null) {
            throw PersoniumCoreException.BarInstall.JSON_FILE_FORMAT_ERROR.params(jsonName);
        } else {
            if (!links.getFromType().equals(Relation.EDM_TYPE_NAME)
                    && !links.getFromType().equals(Role.EDM_TYPE_NAME)
                    && !links.getFromType().equals(ExtRole.EDM_TYPE_NAME)) {
                throw PersoniumCoreException.BarInstall.JSON_FILE_FORMAT_ERROR.params(jsonName);
            }
        }
        if (links.getFromName() == null) {
            throw PersoniumCoreException.BarInstall.JSON_FILE_FORMAT_ERROR.params(jsonName);
        } else {
            Map<String, String> fromNameMap = links.getFromName();
            for (Map.Entry<String, String> entry : fromNameMap.entrySet()) {
                if (entry.getValue() == null) {
                    throw PersoniumCoreException.BarInstall.JSON_FILE_FORMAT_ERROR.params(jsonName);
                }
            }
        }
        if (links.getToType() == null) {
            throw PersoniumCoreException.BarInstall.JSON_FILE_FORMAT_ERROR.params(jsonName);
        } else {
            if (!links.getToType().equals(Relation.EDM_TYPE_NAME) && !links.getToType().equals(Role.EDM_TYPE_NAME)
                    && !links.getToType().equals(ExtRole.EDM_TYPE_NAME)) {
                throw PersoniumCoreException.BarInstall.JSON_FILE_FORMAT_ERROR.params(jsonName);
            }
        }
        if (links.getToName() == null) {
            throw PersoniumCoreException.BarInstall.JSON_FILE_FORMAT_ERROR.params(jsonName);
        } else {
            Map<String, String> toNameMap = links.getToName();
            for (Map.Entry<String, String> entry : toNameMap.entrySet()) {
                if (entry.getValue() == null) {
                    throw PersoniumCoreException.BarInstall.JSON_FILE_FORMAT_ERROR.params(jsonName);
                }
            }
        }
    }

    /**
     * 10_odatarelations.json??.
     * @param jsonName JSON??
     * @param links ??JSON
     */
    private void userDataLinksJsonValidate(String jsonName, JSONUserDataLinks links) {
        if (links.getFromType() == null) {
            throw PersoniumCoreException.BarInstall.JSON_FILE_FORMAT_ERROR.params(jsonName);
        }
        if (links.getFromId() == null) {
            throw PersoniumCoreException.BarInstall.JSON_FILE_FORMAT_ERROR.params(jsonName);
        } else {
            Map<String, String> fromIdMap = links.getFromId();
            for (Map.Entry<String, String> entry : fromIdMap.entrySet()) {
                if (entry.getValue() == null) {
                    throw PersoniumCoreException.BarInstall.JSON_FILE_FORMAT_ERROR.params(jsonName);
                }
            }
        }
        if (links.getToType() == null) {
            throw PersoniumCoreException.BarInstall.JSON_FILE_FORMAT_ERROR.params(jsonName);
        }
        if (links.getToId() == null) {
            throw PersoniumCoreException.BarInstall.JSON_FILE_FORMAT_ERROR.params(jsonName);
        } else {
            Map<String, String> toIdMap = links.getToId();
            for (Map.Entry<String, String> entry : toIdMap.entrySet()) {
                if (entry.getValue() == null) {
                    throw PersoniumCoreException.BarInstall.JSON_FILE_FORMAT_ERROR.params(jsonName);
                }
            }
        }
    }

    /**
     * manifest.json??.
     * @param jp Json
     * @param mapper ObjectMapper
     * @return JSONManifest
     * @throws IOException ???????
     */
    protected JSONManifest manifestJsonValidate(JsonParser jp, ObjectMapper mapper) throws IOException {
        // TODO BAR???
        JSONManifest manifest = null;
        try {
            manifest = mapper.readValue(jp, JSONManifest.class);
        } catch (UnrecognizedPropertyException ex) {
            throw PersoniumCoreException.BarInstall.JSON_FILE_FORMAT_ERROR
                    .params("manifest.json unrecognized property");
        }
        if (manifest.getBarVersion() == null) {
            throw PersoniumCoreException.BarInstall.JSON_FILE_FORMAT_ERROR.params("manifest.json#barVersion");
        }
        if (manifest.getBoxVersion() == null) {
            throw PersoniumCoreException.BarInstall.JSON_FILE_FORMAT_ERROR.params("manifest.json#boxVersion");
        }
        if (manifest.getDefaultPath() == null) {
            throw PersoniumCoreException.BarInstall.JSON_FILE_FORMAT_ERROR.params("manifest.json#DefaultPath");
        }
        return manifest;
    }

    /**
     * ????????????.
     * @param jp
     * @param jsonName
     * @throws IOException
     * @throws JsonParseException
     */
    private void checkMatchFieldName(JsonParser jp, String jsonName) throws IOException {
        String fieldName = jp.getCurrentName();
        if (!(fieldName.equals("Relations") && jsonName.equals(RELATION_JSON))
                && !(fieldName.equals("Roles") && jsonName.equals(ROLE_JSON))
                && !(fieldName.equals("ExtRoles") && jsonName.equals(EXTROLE_JSON))
                && !(fieldName.equals("Links") && jsonName.equals(LINKS_JSON))
                && !(fieldName.equals("Links") && jsonName.equals(USERDATA_LINKS_JSON))) {
            throw PersoniumCoreException.BarInstall.JSON_FILE_FORMAT_ERROR.params(jsonName);
        }
    }

    /**
     * Http??.
     * @param isError ???true????false?.
     * @param code
     *        (personium-messages.properties???)
     * @param path
     *        ?ex. /bar/meta/roles.json)
     */
    private void writeOutputStream(boolean isError, String code, String path) {
        writeOutputStream(isError, code, path, "");
    }

    /**
     * bar?.
     * @param isError ???true????false?.
     * @param code
     *        (personium-messages.properties???)
     * @param path
     *        ?ex. /bar/meta/roles.json)
     * @param detail
     *        ??(PL-BI-2xxx)
     */
    private void writeOutputStream(boolean isError, String code, String path, String detail) {
        String message = PersoniumCoreMessageUtils.getMessage(code);
        if (detail == null) {
            message = message.replace("{0}", "");
        } else {
            message = message.replace("{0}", detail);
        }
        outputEventBus(isError, code, path, message);

        String output = String.format("\"%s\",\"%s\",\"%s\"", code, path, message);
        log.info(output);
    }

    /**
     * ???EventBus????.
     * @param isError ???true????false?.
     * @param code ?ex. PL-BI-0000
     * @param path bar?Edmx????OData?
     * @param message 
     */
    @SuppressWarnings("unchecked")
    private void outputEventBus(boolean isError, String code, String path, String message) {
        if (event != null) {
            event.setAction(code);
            event.setObject(path);
            event.setResult(message);
            eventBus.outputEventLog(event);
        }
        if (this.progressInfo != null && isError) {
            JSONObject messageJson = new JSONObject();
            JSONObject messageDetail = new JSONObject();
            messageJson.put("code", code);
            messageJson.put("message", messageDetail);
            messageDetail.put("lang", "en");
            messageDetail.put("value", message);
            this.progressInfo.setMessage(messageJson);
            writeToProgressCache(true);
        } else {
            writeToProgressCache(false);
        }
    }

    /**
     * ?bar??.
     * @param forceOutput ????true????false?
     */
    private void writeToProgressCache(boolean forceOutput) {
        if (this.progressInfo != null && this.progressInfo.isOutputEventBus() || forceOutput) {
            String key = "box-" + this.box.getId();
            Progress progress = new Progress(key, progressInfo.toString());
            ProgressManager.putProgress(key, progress);
            log.info("Progress(" + key + "): " + progressInfo.toString());
        }
    }

    private void setupBarFileOrder() {
        barFileOrder = new LinkedHashMap<String, Boolean>();
        barFileOrder.put("bar/00_meta/00_manifest.json", true);
        barFileOrder.put("bar/00_meta/10_relations.json", false);
        barFileOrder.put("bar/00_meta/20_roles.json", false);
        barFileOrder.put("bar/00_meta/30_extroles.json", false);
        barFileOrder.put("bar/00_meta/70_$links.json", false);
        barFileOrder.put("bar/00_meta/90_rootprops.xml", true);
        barFileOrder.put("bar/90_contents/", false); // dummy
    }

    private String createdBoxEtag = "";
    private String createdBoxName = "";

    /**
     * ???Box????.
     * @return ??
     */
    public String getCreatedBoxName() {
        return createdBoxName;
    }

    /**
     * ???Box?ETag?.
     * @return ETag
     */
    public String getCreatedBoxETag() {
        return createdBoxEtag;
    }

    /**
     * BoxES??.
     * @param json JSON???JSON
     */
    @SuppressWarnings("unchecked")
    void createBox(JSONObject json) {
        if (boxName == null || boxName.isEmpty()) {
            this.createdBoxName = (String) json.get("Name");
        } else {
            json.put("Name", boxName);
            this.createdBoxName = boxName;
        }
        StringReader stringReader = new StringReader(json.toJSONString());

        OEntityWrapper oew = odataEntityResource.getOEntityWrapper(stringReader,
                odataEntityResource.getOdataResource(), CtlSchema.getEdmDataServicesForCellCtl().build());

        // Box?
        odataProducer.createEntity(entitySetName, oew);
        this.createdBoxEtag = oew.getEtag();

        // Dav?
        Box newBox = new Box(odataEntityResource.getAccessContext().getCell(), oew);
        this.boxCmp = ModelFactory.boxCmp(newBox);

        this.box = newBox;
        this.schemaUrl = (String) json.get("Schema");
    }

    /**
     * 10_$relations.json????RelationES??.
     * @param json JSON???JSON
     */
    @SuppressWarnings("unchecked")
    private void createRelation(JSONObject json) {
        json.put("_Box.Name", createdBoxName);
        StringReader stringReader = new StringReader(json.toJSONString());

        odataEntityResource.setEntitySetName(Relation.EDM_TYPE_NAME);
        OEntityWrapper oew = odataEntityResource.getOEntityWrapper(stringReader,
                odataEntityResource.getOdataResource(), CtlSchema.getEdmDataServicesForCellCtl().build());

        // Relation?
        odataProducer.createEntity(Relation.EDM_TYPE_NAME, oew);
    }

    /**
     * 20_$roles.json????RoleES??.
     * @param json JSON???JSON
     */
    @SuppressWarnings("unchecked")
    private void createRole(JSONObject json) {
        json.put("_Box.Name", createdBoxName);
        StringReader stringReader = new StringReader(json.toJSONString());

        odataEntityResource.setEntitySetName(Role.EDM_TYPE_NAME);
        OEntityWrapper oew = odataEntityResource.getOEntityWrapper(stringReader,
                odataEntityResource.getOdataResource(), CtlSchema.getEdmDataServicesForCellCtl().build());

        // Role?
        odataProducer.createEntity(Role.EDM_TYPE_NAME, oew);
    }

    /**
     * 30_$extroles.json????ExtRoleES??.
     * @param json JSON???JSON
     */
    @SuppressWarnings("unchecked")
    private void createExtRole(JSONObject json) {
        String url = (String) json.get(ExtRole.EDM_TYPE_NAME);
        json.put(ExtRole.EDM_TYPE_NAME, url);
        json.put("_Relation._Box.Name", createdBoxName);
        StringReader stringReader = new StringReader(json.toJSONString());

        odataEntityResource.setEntitySetName(ExtRole.EDM_TYPE_NAME);
        OEntityWrapper oew = odataEntityResource.getOEntityWrapper(stringReader,
                odataEntityResource.getOdataResource(), CtlSchema.getEdmDataServicesForCellCtl().build());

        // ExtRole?
        odataProducer.createEntity(ExtRole.EDM_TYPE_NAME, oew);
    }

    /**
     * 70_$links.json????ES??.
     * @param mappedObject JSON???
     */
    private void createLinks(JSONMappedObject mappedObject, PersoniumODataProducer producer) {
        Map<String, String> fromNameMap = ((JSONLinks) mappedObject).getFromName();
        String fromkey = BarFileUtils.getComplexKeyName(((JSONLinks) mappedObject).getFromType(), fromNameMap,
                this.boxName);
        OEntityKey fromOEKey = OEntityKey.parse(fromkey);
        OEntityId sourceEntity = OEntityIds.create(((JSONLinks) mappedObject).getFromType(), fromOEKey);
        String targetNavProp = ((JSONLinks) mappedObject).getNavPropToType();

        // ???
        odataEntityResource.getOdataResource().beforeLinkCreate(sourceEntity, targetNavProp);

        Map<String, String> toNameMap = ((JSONLinks) mappedObject).getToName();
        String tokey = BarFileUtils.getComplexKeyName(((JSONLinks) mappedObject).getToType(), toNameMap,
                this.boxName);
        OEntityKey toOEKey = OEntityKey.parse(tokey);
        OEntityId newTargetEntity = OEntityIds.create(((JSONLinks) mappedObject).getToType(), toOEKey);
        // $links?
        producer.createLink(sourceEntity, targetNavProp, newTargetEntity);
    }

    private boolean createUserdataLinks(PersoniumODataProducer producer, List<JSONMappedObject> userDataLinks) {
        int linkSize = userDataLinks.size();
        int linkCount = 0;
        String message = PersoniumCoreMessageUtils.getMessage("PL-BI-1002");
        for (JSONMappedObject json : userDataLinks) {
            linkCount++;
            if (!createUserdataLink(json, producer)) {
                return false;
            }
            if (linkCount % linksOutputStreamSize == 0) {
                writeOutputStream(false, "PL-BI-1002", String.format("userDataLinks %d / %d", linkCount, linkSize),
                        message);
            }
        }
        writeOutputStream(false, "PL-BI-1002", String.format("userDataLinks %d / %d", linkCount, linkSize),
                message);
        return true;
    }

    /**
     * 10_odatarelations.json????ES??.
     * @param mappedObject JSON???
     */
    private boolean createUserdataLink(JSONMappedObject mappedObject, PersoniumODataProducer producer) {
        OEntityId sourceEntity = null;
        OEntityId newTargetEntity = null;
        try {
            Map<String, String> fromId = ((JSONUserDataLinks) mappedObject).getFromId();
            String fromKey = "";
            Iterator<Entry<String, String>> fromIterator = fromId.entrySet().iterator();
            fromKey = String.format("('%s')", fromIterator.next().getValue());

            OEntityKey fromOEKey = OEntityKey.parse(fromKey);
            sourceEntity = OEntityIds.create(((JSONUserDataLinks) mappedObject).getFromType(), fromOEKey);
            String targetNavProp = ((JSONUserDataLinks) mappedObject).getNavPropToType();

            // ???
            odataEntityResource.getOdataResource().beforeLinkCreate(sourceEntity, targetNavProp);

            Map<String, String> toId = ((JSONUserDataLinks) mappedObject).getToId();
            String toKey = "";
            Iterator<Entry<String, String>> toIterator = toId.entrySet().iterator();
            toKey = String.format("('%s')", toIterator.next().getValue());

            OEntityKey toOEKey = OEntityKey.parse(toKey);
            newTargetEntity = OEntityIds.create(((JSONUserDataLinks) mappedObject).getToType(), toOEKey);

            // $links?
            producer.createLink(sourceEntity, targetNavProp, newTargetEntity);
        } catch (Exception e) {
            String path = "";
            String targetPath = "";
            if (sourceEntity != null) {
                path = sourceEntity.getEntitySetName() + sourceEntity.getEntityKey();
            }
            if (newTargetEntity != null) {
                targetPath = "Target Link to " + newTargetEntity.getEntitySetName()
                        + newTargetEntity.getEntityKey();
            }
            log.info(e.getMessage() + " [" + path + "]", e.fillInStackTrace());
            writeOutputStream(true, "PL-BI-1004", path, targetPath);
            return false;
        }
        return true;
    }

    private void createCollection(String collectionUrl, String entryName, Cell parentCell, Box parentBox,
            int collectionType, Element aclElement, List<Element> propElements) {
        int index;
        if (parentCell == null || parentBox == null) {
            return;
        }

        String type = "";
        switch (collectionType) {
        case TYPE_WEBDAV_COLLECTION:
            type = DavCmp.TYPE_COL_WEBDAV;
            break;
        case TYPE_ODATA_COLLECTION:
            type = DavCmp.TYPE_COL_ODATA;
            break;
        case TYPE_SERVICE_COLLECTION:
            type = DavCmp.TYPE_COL_SVC;
            break;
        default:
            break;
        }

        String parenEntryName = "";
        DavCmp parentCmp = null;
        String tmp = entryName.replace(CONTENTS_DIR, "/");
        String[] slash = tmp.split("/");
        if (slash.length == 2) {
            parentCmp = this.boxCmp;
        } else {
            index = entryName.lastIndexOf("/", entryName.length() - 2);
            parenEntryName = entryName.substring(0, index + 1);
            parentCmp = this.davCmpMap.get(parenEntryName);
            if (parentCmp == null) {
                throw PersoniumCoreException.BarInstall.JSON_FILE_FORMAT_ERROR.params(entryName);
            } else if (parentCmp.getType().equals(DavCmp.TYPE_COL_ODATA)) {
                throw PersoniumCoreException.BarInstall.JSON_FILE_FORMAT_ERROR.params(entryName);
            } else if (parentCmp.getType().equals(DavCmp.TYPE_COL_SVC)) {
                String crrName = entryName.substring(index + 1, entryName.length() - 1);
                if (!"__src".equals(crrName)) {
                    throw PersoniumCoreException.BarInstall.JSON_FILE_FORMAT_ERROR.params(entryName);
                }
            }
        }
        //      String parentId = parentCmp.getId();

        //        // ???
        //        DavCmp current = parentCmp;
        //
        //        // current???????????depth???1
        //        int depth = 1;
        //        int maxDepth = PersoniumCoreConfig.getMaxCollectionDepth();
        //        while (null != current.getParent()) {
        //            current = (DavCmp) current.getParent();
        //            depth++;
        //        }
        //        if (depth > maxDepth) {
        //            // ???????400??
        //            throw PersoniumCoreException.Dav.COLLECTION_DEPTH_ERROR;
        //        }
        //
        //        // ???
        //        int maxChildResource = PersoniumCoreConfig.getMaxChildResourceCount();
        //        if (parentCmp.getChildrenCount() >= maxChildResource) {
        //            // ???????????400??
        //            throw PersoniumCoreException.Dav.COLLECTION_CHILDRESOURCE_ERROR;
        //        }

        //        // ??
        String collectionName = "";
        index = collectionUrl.lastIndexOf("/");
        collectionName = collectionUrl.substring(index + 1);

        DavCmp collectionCmp = parentCmp.getChild(collectionName);
        collectionCmp.mkcol(type);

        this.davCmpMap.put(entryName, collectionCmp);

        // ACL
        if (aclElement != null) {
            String baseUrl = uriInfo.getBaseUri().toASCIIString();
            Element convElement = BarFileUtils.convertToRoleInstanceUrl(aclElement, baseUrl, this.cell.getName(),
                    this.boxName);
            StringBuffer sbAclXml = new StringBuffer();
            sbAclXml.append("<?xml version=\"1.0\" encoding=\"utf-8\" ?>");
            sbAclXml.append(PersoniumCoreUtils.nodeToString(convElement));
            Reader aclXml = new StringReader(sbAclXml.toString());
            collectionCmp.acl(aclXml);
        }

        // PROPPATCH
        registProppatch(collectionCmp, propElements, collectionUrl);
    }

    /**
     * Box?ACL?PROPPATCH.
     * @param targetBox box
     * @param aclElement ACL
     * @param propElements PROPATCH??
     * @param boxUrl box?URL
     */
    private void registBoxAclAndProppatch(Box targetBox, Element aclElement, List<Element> propElements,
            String boxUrl) {
        if (targetBox == null || boxCmp == null) {
            return;
        }

        // ACL
        if (aclElement != null) {
            String baseUrl = uriInfo.getBaseUri().toASCIIString();
            Element convElement = BarFileUtils.convertToRoleInstanceUrl(aclElement, baseUrl, this.cell.getName(),
                    this.boxName);
            StringBuffer sbAclXml = new StringBuffer();
            sbAclXml.append("<?xml version=\"1.0\" encoding=\"utf-8\" ?>");
            sbAclXml.append(PersoniumCoreUtils.nodeToString(convElement));
            Reader aclXml = new StringReader(sbAclXml.toString());
            boxCmp.acl(aclXml);
        }

        // PROPPATCH
        registProppatch(boxCmp, propElements, boxUrl);
    }

    private void registProppatch(DavCmp davCmp, List<Element> propElements, String boxUrl) {
        if (!propElements.isEmpty()) {
            Reader propXml = getProppatchXml(propElements);
            try {
                Propertyupdate propUpdate = Propertyupdate.unmarshal(propXml);
                davCmp.proppatch(propUpdate, boxUrl);
            } catch (IOException ex) {
                throw PersoniumCoreException.Dav.XML_ERROR.reason(ex);
            }
        }
    }

    /**
     * bar?????Map<key, DavCmpEsImpl>??.
     * @return ?MapDavCmpEsImpl
     */
    private Map<String, DavCmp> getCollections(String colType) {
        Map<String, DavCmp> map = new HashMap<String, DavCmp>();
        Set<String> keySet = davCmpMap.keySet();
        for (String key : keySet) {
            DavCmp davCmp = davCmpMap.get(key);
            if (davCmp != null && colType.equals(davCmp.getType())) {
                map.put(key, davCmp);
            }
        }
        return map;
    }

    /**
     * bar?????Map<key, DavCmpEsImpl>??.
     * @param entryName ??
     * @param collections ?Map
     * @return ?MapDavCmpEsImpl
     */
    private DavCmp getCollection(String entryName, Map<String, DavCmp> collections) {
        int pos = entryName.lastIndexOf("/");
        if (pos == entryName.length() - 1) {
            return collections.get(entryName);
        }
        String colName = entryName.substring(0, pos + 1);
        return collections.get(colName);
    }

    /**
     * 00_$metadata_xml??????.
     * @param entryName ??
     * @param inputStream 
     * @param davCmp Collection?
     * @return ????true
     */
    protected boolean registUserSchema(String entryName, InputStream inputStream, DavCmp davCmp) {
        EdmDataServices metadata = null;
        // XML(StAX,SAX,DOM)?InputStream???????????
        // ?????????????????
        try {
            InputStreamReader isr = new InputStreamReader(new CloseShieldInputStream(inputStream));
            // 00_$metadata.xml????
            XMLFactoryProvider2 provider = StaxXMLFactoryProvider2.getInstance();
            XMLInputFactory2 factory = provider.newXMLInputFactory2();
            XMLEventReader2 reader = factory.createXMLEventReader(isr);
            PersoniumEdmxFormatParser parser = new PersoniumEdmxFormatParser();
            metadata = parser.parseMetadata(reader);
        } catch (Exception ex) {
            log.info("XMLParseException: " + ex.getMessage(), ex.fillInStackTrace());
            String message = PersoniumCoreMessageUtils.getMessage("PL-BI-2002");
            writeOutputStream(true, "PL-BI-1004", entryName, message);
            return false;
        } catch (StackOverflowError tw) {
            // ComplexType???StackOverFlowError??
            log.info("XMLParseException: " + tw.getMessage(), tw.fillInStackTrace());
            String message = PersoniumCoreMessageUtils.getMessage("PL-BI-2002");
            writeOutputStream(true, "PL-BI-1004", entryName, message);
            return false;
        }
        // Entity/Property?
        // Property/ComplexProperty????ComplexType???????
        // ??ComplexType????EntityType?
        // PersoniumODataProducer producer = davCmp.getODataProducer();
        try {
            createComplexTypes(metadata, davCmp);
            createEntityTypes(metadata, davCmp);
            createAssociations(metadata, davCmp);
        } catch (PersoniumCoreException e) {
            writeOutputStream(true, "PL-BI-1004", entryName, e.getMessage());
            log.info("PersoniumCoreException: " + e.getMessage());
            return false;
        } catch (Exception e) {
            log.info("Regist Entity Error: " + e.getMessage(), e.fillInStackTrace());
            String message = PersoniumCoreMessageUtils.getMessage("PL-BI-2003");
            writeOutputStream(true, "PL-BI-1004", entryName, message);
            return false;
        }
        return true;
    }

    /**
     * Edmx????EntityType/Property?.
     * @param metadata Edmx?
     * @param davCmp Collection?
     */
    @SuppressWarnings("unchecked")
    protected void createEntityTypes(EdmDataServices metadata, DavCmp davCmp) {
        // DeclaredProperty?EntityType????????EntityType???Property?
        Iterable<EdmEntityType> entityTypes = metadata.getEntityTypes();
        UserSchemaODataProducer producer = null;
        EdmDataServices userMetadata = null;
        for (EdmEntityType entity : entityTypes) {
            log.debug("EntityType: " + entity.getName());
            if (producer == null) {
                producer = (UserSchemaODataProducer) davCmp.getSchemaODataProducer(this.cell);
                userMetadata = CtlSchema.getEdmDataServicesForODataSvcSchema().build();
            }
            Map<String, String> entityTypeIds = producer.getEntityTypeIds();
            odataEntityResource.setEntitySetName(EntityType.EDM_TYPE_NAME);
            JSONObject json = new JSONObject();
            json.put("Name", entity.getName());
            StringReader stringReader = new StringReader(json.toJSONString());
            OEntityWrapper oew = odataEntityResource.getOEntityWrapper(stringReader,
                    odataEntityResource.getOdataResource(), userMetadata);
            // EntityType?
            String path = String.format("/%s/%s/%s/EntityType('%s')", this.cell.getName(), this.boxName,
                    davCmp.getName(), entity.getName());
            writeOutputStream(false, "PL-BI-1002", path);
            EntityResponse response = producer.createEntity(EntityType.EDM_TYPE_NAME, oew);
            OEntityWrapper entityResponse = (OEntityWrapper) response.getEntity();
            entityTypeIds.put(entity.getName(), entityResponse.getUuid());
        }
        // EntityType?????Property?
        for (EdmEntityType entity : entityTypes) {
            createProperties(entity, davCmp, producer);
        }
    }

    /**
     * Edmx????Property/ComplexTypeProperty?.
     * @param entity ?Property????EntityType/ComplexType
     * @param davCmp Collection?
     * @param producer OData
     */
    @SuppressWarnings("unchecked")
    protected void createProperties(EdmStructuralType entity, DavCmp davCmp, PersoniumODataProducer producer) {
        Iterable<EdmProperty> properties = entity.getDeclaredProperties();
        EdmDataServices userMetadata = null;
        String edmTypeName = Property.EDM_TYPE_NAME;
        if (entity instanceof EdmComplexType) {
            edmTypeName = ComplexTypeProperty.EDM_TYPE_NAME;
        }
        for (EdmProperty property : properties) {
            String name = property.getName();
            log.debug(edmTypeName + ": " + name);
            if (name.startsWith("_")) {
                continue;
            }
            if (userMetadata == null) {
                userMetadata = CtlSchema.getEdmDataServicesForODataSvcSchema().build();
                odataEntityResource.setEntitySetName(edmTypeName);
            }
            CollectionKind kind = property.getCollectionKind();
            if (kind != null && !kind.equals(CollectionKind.NONE) && !kind.equals(CollectionKind.List)) {
                throw PersoniumCoreException.BarInstall.JSON_FILE_FORMAT_ERROR.params(METADATA_XML);
            }
            JSONObject json = new JSONObject();
            json.put("Name", property.getName());
            if (entity instanceof EdmComplexType) {
                json.put("_ComplexType.Name", entity.getName());
            } else {
                json.put("_EntityType.Name", entity.getName());
                json.put("IsKey", false); // Iskey????
                json.put("UniqueKey", null); // UniqueKey????
            }
            String typeName = property.getType().getFullyQualifiedTypeName();
            if (!property.getType().isSimple() && typeName.startsWith("UserData.")) {
                typeName = typeName.replace("UserData.", "");
            }
            json.put("Type", typeName);
            json.put("Nullable", property.isNullable());
            json.put("DefaultValue", property.getDefaultValue());
            json.put("CollectionKind", property.getCollectionKind().toString());
            StringReader stringReader = new StringReader(json.toJSONString());
            OEntityWrapper oew = odataEntityResource.getOEntityWrapper(stringReader,
                    odataEntityResource.getOdataResource(), userMetadata);
            // ComplexTypeProperty?
            producer.createEntity(edmTypeName, oew);
        }
    }

    /**
     * Edmx????AssociationEnd???.
     * @param metadata Edmx?
     * @param davCmp Collection?
     */
    protected void createAssociations(EdmDataServices metadata, DavCmp davCmp) {
        Iterable<EdmAssociation> associations = metadata.getAssociations();
        PersoniumODataProducer producer = null;
        EdmDataServices userMetadata = null;
        for (EdmAssociation association : associations) {
            // Association???AssociationEnd?AssociationEnd???
            String name = association.getName();
            log.debug("Association: " + name);
            if (producer == null) {
                producer = davCmp.getSchemaODataProducer(this.cell);
                userMetadata = CtlSchema.getEdmDataServicesForODataSvcSchema().build();
                odataEntityResource.setEntitySetName(AssociationEnd.EDM_TYPE_NAME);
            }
            String path = String.format("/%s/%s/%s/Association('%s','%s')", this.cell.getName(), this.boxName,
                    davCmp.getName(), association.getEnd1().getRole(), association.getEnd2().getRole());
            writeOutputStream(false, "PL-BI-1002", path);
            // AssociationEnd?
            EdmAssociationEnd ae1 = association.getEnd1();
            String realRoleName1 = getRealRoleName(ae1.getRole());
            createAssociationEnd(producer, userMetadata, ae1, realRoleName1);
            EdmAssociationEnd ae2 = association.getEnd2();
            String realRoleName2 = getRealRoleName(ae2.getRole());
            createAssociationEnd(producer, userMetadata, ae2, realRoleName2);

            // AssociationEnd?$links
            String fromkey = String.format("(Name='%s',_EntityType.Name='%s')", realRoleName1,
                    ae1.getType().getName());
            OEntityKey fromOEKey = OEntityKey.parse(fromkey);
            OEntityId sourceEntity = OEntityIds.create(AssociationEnd.EDM_TYPE_NAME, fromOEKey);
            String targetNavProp = "_" + AssociationEnd.EDM_TYPE_NAME;
            odataEntityResource.getOdataResource().beforeLinkCreate(sourceEntity, targetNavProp);
            String tokey = String.format("(Name='%s',_EntityType.Name='%s')", realRoleName2,
                    ae2.getType().getName());
            OEntityKey toOEKey = OEntityKey.parse(tokey);
            OEntityId newTargetEntity = OEntityIds.create(AssociationEnd.EDM_TYPE_NAME, toOEKey);
            producer.createLink(sourceEntity, targetNavProp, newTargetEntity);
        }
    }

    /*
     * ("??:??")??????
     * ???????????
     * @param sourceRoleName ???? ("??:??")
     * @return ???
     */
    private String getRealRoleName(String sourceRoleName) {
        String[] tokens = sourceRoleName.split(":");
        if (tokens.length != 2) {
            throw PersoniumCoreException.BarInstall.JSON_FILE_FORMAT_ERROR.params(sourceRoleName);
        }
        if (tokens[0].length() <= 0 || tokens[1].length() <= 0) {
            throw PersoniumCoreException.BarInstall.JSON_FILE_FORMAT_ERROR.params(sourceRoleName);
        }
        return tokens[1];
    }

    /**
     * ???AssociationEnd?.
     * @param producer EntityPersoniumODataProcucer
     * @param userMetadata 
     * @param associationEnd AssociationEnd
     * @param associationEndName AssociationEnd??
     */
    @SuppressWarnings("unchecked")
    protected void createAssociationEnd(PersoniumODataProducer producer, EdmDataServices userMetadata,
            EdmAssociationEnd associationEnd, String associationEndName) {
        // AssociationEnd??????AssociationEnd????
        JSONObject json = new JSONObject();
        String entityTypeName = associationEnd.getType().getName();
        json.put(AssociationEnd.P_ASSOCIATION_NAME.getName(), associationEndName);
        json.put(AssociationEnd.P_ENTITYTYPE_NAME.getName(), entityTypeName);
        json.put(AssociationEnd.P_MULTIPLICITY.getName(), associationEnd.getMultiplicity().getSymbolString());
        StringReader stringReader = new StringReader(json.toJSONString());
        OEntityWrapper oew = odataEntityResource.getOEntityWrapper(stringReader,
                odataEntityResource.getOdataResource(), userMetadata);
        producer.createEntity(AssociationEnd.EDM_TYPE_NAME, oew);
    }

    /**
     * Edmx????ComplexType/ComplexTypeProperty?.
     * @param metadata Edmx?
     * @param davCmp Collection?
     */
    @SuppressWarnings("unchecked")
    protected void createComplexTypes(EdmDataServices metadata, DavCmp davCmp) {
        // DeclaredProperty?ComplexType????????ComplexType???ComplexTypeProperty?
        Iterable<EdmComplexType> complexTypes = metadata.getComplexTypes();
        PersoniumODataProducer producer = null;
        EdmDataServices userMetadata = null;
        for (EdmComplexType complexType : complexTypes) {
            log.debug("ComplexType: " + complexType.getName());
            if (producer == null) {
                producer = davCmp.getSchemaODataProducer(this.cell);
                userMetadata = CtlSchema.getEdmDataServicesForODataSvcSchema().build();
            }
            odataEntityResource.setEntitySetName(ComplexType.EDM_TYPE_NAME);
            JSONObject json = new JSONObject();
            json.put("Name", complexType.getName());
            StringReader stringReader = new StringReader(json.toJSONString());
            OEntityWrapper oew = odataEntityResource.getOEntityWrapper(stringReader,
                    odataEntityResource.getOdataResource(), userMetadata);
            // ComplexType?
            String path = String.format("/%s/%s/%s/ComplexType('%s')", this.cell.getName(), this.boxName,
                    davCmp.getName(), complexType.getName());
            writeOutputStream(false, "PL-BI-1002", path);
            producer.createEntity(ComplexType.EDM_TYPE_NAME, oew);
        }

        // ComplexType?????ComplexTypeProperty?
        for (EdmComplexType complexType : complexTypes) {
            createProperties(complexType, davCmp, producer);
        }
    }

    private Reader getProppatchXml(List<Element> propElements) {
        StringBuffer sbPropXml = new StringBuffer();
        sbPropXml.append("<D:propertyupdate xmlns:D=\"DAV:\"");
        sbPropXml.append(" xmlns:p=\"urn:x-personium:xmlns\"");
        sbPropXml.append(" xmlns:Z=\"http://www.w3.com/standards/z39.50/\">");
        sbPropXml.append("<D:set>");
        sbPropXml.append("<D:prop>");
        for (Element element : propElements) {
            sbPropXml.append(PersoniumCoreUtils.nodeToString(element));
        }
        sbPropXml.append("</D:prop>");
        sbPropXml.append("</D:set>");
        sbPropXml.append("</D:propertyupdate>");
        Reader propXml = new StringReader(sbPropXml.toString());
        return propXml;
    }

    /**
     * @param entryCount the entryCount to set
     */
    void setEntryCount(long entryCount) {
        this.progressInfo = new BarInstallProgressInfo(this.cell.getId(), this.box.getId(), entryCount);
    }

    /**
     * ??????.
     */
    public void writeInitProgressCache() {
        setEventBus();
        writeOutputStream(false, "PL-BI-1000", this.cell.getUrl() + boxName, "");
        writeToProgressCache(true);
    }

    /**
     * ??.
     */
    public void writeErrorProgressCache() {
        if (this.progressInfo != null) {
            this.progressInfo.setStatus(ProgressInfo.STATUS.FAILED);
            this.progressInfo.setEndTime();
            writeToProgressCache(true);
        }
    }

}