com.fujitsu.dc.core.model.impl.es.DavCmpEsImpl.java Source code

Java tutorial

Introduction

Here is the source code for com.fujitsu.dc.core.model.impl.es.DavCmpEsImpl.java

Source

/**
 * personium.io
 * Copyright 2014 FUJITSU LIMITED
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.fujitsu.dc.core.model.impl.es;

import java.io.BufferedInputStream;
import java.io.ByteArrayInputStream;
import java.io.InputStream;
import java.io.Reader;
import java.io.UnsupportedEncodingException;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLEncoder;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

import javax.ws.rs.WebApplicationException;
import javax.ws.rs.core.HttpHeaders;
import javax.ws.rs.core.Response.ResponseBuilder;
import javax.ws.rs.core.StreamingOutput;
import javax.xml.namespace.QName;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;

import org.apache.commons.codec.CharEncoding;
import org.apache.http.HttpStatus;
import org.apache.wink.webdav.model.Creationdate;
import org.apache.wink.webdav.model.Getcontentlength;
import org.apache.wink.webdav.model.Getcontenttype;
import org.apache.wink.webdav.model.Getlastmodified;
import org.apache.wink.webdav.model.Multistatus;
import org.apache.wink.webdav.model.ObjectFactory;
import org.apache.wink.webdav.model.Prop;
import org.apache.wink.webdav.model.Propertyupdate;
import org.apache.wink.webdav.model.Propfind;
import org.apache.wink.webdav.model.Resourcetype;
import org.apache.wink.webdav.model.Response;
import org.apache.wink.webdav.model.WebDAVModelHelper;
import org.json.simple.JSONArray;
import org.json.simple.JSONObject;
import org.json.simple.parser.JSONParser;
import org.json.simple.parser.ParseException;
import org.odata4j.producer.CountResponse;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NodeList;

import com.fujitsu.dc.common.auth.token.Role;
import com.fujitsu.dc.common.es.response.DcActionResponse;
import com.fujitsu.dc.common.es.response.DcGetResponse;
import com.fujitsu.dc.common.es.response.DcIndexResponse;
import com.fujitsu.dc.common.es.response.DcSearchHit;
import com.fujitsu.dc.common.es.response.DcSearchHits;
import com.fujitsu.dc.common.es.response.DcSearchResponse;
import com.fujitsu.dc.common.es.util.DcUUID;
import com.fujitsu.dc.common.es.util.IndexNameEncoder;
import com.fujitsu.dc.common.utils.DcCoreUtils;
import com.fujitsu.dc.core.DcCoreConfig;
import com.fujitsu.dc.core.DcCoreException;
import com.fujitsu.dc.core.DcCoreLog;
import com.fujitsu.dc.core.auth.AccessContext;
import com.fujitsu.dc.core.auth.OAuth2Helper.Key;
import com.fujitsu.dc.core.http.header.ByteRangeSpec;
import com.fujitsu.dc.core.http.header.RangeHeaderHandler;
import com.fujitsu.dc.core.model.Box;
import com.fujitsu.dc.core.model.Cell;
import com.fujitsu.dc.core.model.DavCmp;
import com.fujitsu.dc.core.model.DavNode;
import com.fujitsu.dc.core.model.ModelFactory;
import com.fujitsu.dc.core.model.ctl.ComplexType;
import com.fujitsu.dc.core.model.ctl.EntityType;
import com.fujitsu.dc.core.model.file.BinaryDataAccessException;
import com.fujitsu.dc.core.model.file.BinaryDataAccessor;
import com.fujitsu.dc.core.model.file.BinaryDataNotFoundException;
import com.fujitsu.dc.core.model.file.StreamingOutputForDavFile;
import com.fujitsu.dc.core.model.file.StreamingOutputForDavFileWithRange;
import com.fujitsu.dc.core.model.impl.es.accessor.DavNodeAccessor;
import com.fujitsu.dc.core.model.impl.es.accessor.EntitySetAccessor;
import com.fujitsu.dc.core.model.impl.es.doc.EsDocHandler;
import com.fujitsu.dc.core.model.impl.es.odata.UserSchemaODataProducer;
import com.fujitsu.dc.core.model.jaxb.Acl;
import com.fujitsu.dc.core.model.jaxb.ObjectIo;
import com.fujitsu.dc.core.model.lock.Lock;
import com.fujitsu.dc.core.model.lock.LockManager;
import com.fujitsu.dc.core.odata.DcODataProducer;

/**
 * DavCmp?Elastic Search.
 */
public class DavCmpEsImpl implements DavCmp, EsDocHandler {
    String nodeId;
    Box box;
    Cell cell;
    DavNode davNode;
    Long version;
    ObjectFactory of;
    String name;
    Acl acl;
    DavCmpEsImpl parent;
    String confidentialLevel;
    List<String> ownerRepresentativeAccounts = new ArrayList<String>();

    /**
     * Es???.
     */
    private static final int TOP_NUM = DcCoreConfig.getEsTopNum();

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

    DavCmpEsImpl() {
    }

    /**
     * Box?.
     * @return ?
     */
    public Lock lock() {
        return LockManager.getLock(Lock.CATEGORY_DAV, null, this.box.getId(), null);
    }

    /**
     * @return ETag.
     */
    @Override
    public String getEtag() {
        StringBuilder sb = new StringBuilder("\"");
        sb.append(this.version);
        sb.append("-");
        sb.append(this.davNode.getUpdated());
        sb.append("\"");
        return sb.toString();
    }

    /**
     * .
     * @param name ????
     * @param parent ?
     * @param cell Cell
     * @param box Box
     * @param nodeId ID
     */
    public DavCmpEsImpl(final String name, final DavCmpEsImpl parent, final Cell cell, final Box box,
            final String nodeId) {
        this.cell = cell;
        this.box = box;
        this.name = name;
        this.parent = parent;
        this.nodeId = nodeId;
        this.of = new ObjectFactory();
        if (this.nodeId != null) {
            this.load();
        }
    }

    @Override
    public boolean isEmpty() {

        String type = this.getType();
        if (DavCmp.TYPE_COL_WEBDAV.equals(type)) {
            return !(this.getChildrenCount() > 0);
        } else if (DavCmp.TYPE_COL_BOX.equals(type)) {
            return !(this.getChildrenCount() > 0);
        } else if (DavCmp.TYPE_COL_ODATA.equals(type)) {
            // Collection?????EntityType???
            // EntityType?????(AsssociationEnd??)?EntityType????????
            // EntityType?????EntityType?????????????
            UserSchemaODataProducer producer = new UserSchemaODataProducer(this.cell, this);
            CountResponse cr = producer.getEntitiesCount(EntityType.EDM_TYPE_NAME, null);
            if (cr.getCount() > 0) {
                return false;
            }
            // Collection?????ComplexType???
            // ComplexType?????(ComplexTypeProperty)?ComplexType????????
            // ComplexType?????ComplexType?????????????
            cr = producer.getEntitiesCount(ComplexType.EDM_TYPE_NAME, null);
            return cr.getCount() < 1;
        } else if (DavCmp.TYPE_COL_SVC.equals(type)) {
            return !(this.getChild(SERVICE_SRC_COLLECTION).getChildrenCount() > 0);
        }
        DcCoreLog.Misc.UNREACHABLE_CODE_ERROR.writeLog();
        throw DcCoreException.Server.UNKNOWN_ERROR;
    }

    @Override
    public void makeEmpty() {
        // TODO 
    }

    /**
     * ACL?getter.
     * @return acl
     */
    public Acl getAcl() {
        return this.acl;
    }

    /**
     * ???.
     * @return ?
     */
    public String getConfidentialLevel() {
        return this.confidentialLevel;
    }

    /**
     * ??.
     * @return ?
     */
    public List<String> getOwnerRepresentativeAccounts() {
        return this.ownerRepresentativeAccounts;
    }

    /**
     * Cell????.
     * @return Cell???true?
     */
    public boolean isCellLevel() {
        if (this.box != null) {
            return false;
        }
        return true;
    }

    /**
     * ???.
     */
    @SuppressWarnings("unchecked")
    public final void load() {
        DcGetResponse res = getNode();
        if (res == null) {
            // Box???id????Dav????????
            throw DcCoreException.Dav.DAV_INCONSISTENCY_FOUND;
        }
        this.version = res.version();
        this.davNode = DavNode.createFromJsonString(res.getId(), res.sourceAsString());
        if (this.davNode != null) {
            // Map<String, Object> aclObj = (Map<String, Object>) json.get("acl");
            // TODO JSONParse?????. JSON Parse?????????.
            String jsonStr = res.sourceAsString();
            JSONParser parser = new JSONParser();
            try {
                JSONObject jo = (JSONObject) parser.parse(jsonStr);
                JSONObject aclObj = (JSONObject) jo.get(DavNode.KEY_ACL);
                if (aclObj != null) {
                    log.debug(aclObj.toJSONString());
                    // principal?href ? ID__id?URL???
                    // base:xml?
                    String baseUrlStr = createBaseUrlStr();
                    roleIdToName(aclObj.get(KEY_ACE), baseUrlStr);

                    // ConfidentialLevel???
                    this.confidentialLevel = (String) aclObj.get(KEY_REQUIRE_SCHEMA_AUTHZ);

                    this.acl = Acl.fromJson(aclObj.toJSONString());
                    this.acl.setBase(baseUrlStr);
                    log.debug(this.acl.toJSON());
                }

                Map<String, String> props = (Map<String, String>) jo.get(DavNode.KEY_PROPS);
                if (props != null) {
                    for (Map.Entry<String, String> entry : props.entrySet()) {
                        String key = entry.getKey();
                        String val = entry.getValue();
                        int idx = key.indexOf("@");
                        String elementName = key.substring(0, idx);
                        String namespace = key.substring(idx + 1);
                        QName keyQName = new QName(namespace, elementName);

                        Element element = parseProp(val);
                        String elementNameSpace = element.getNamespaceURI();
                        // ownerRepresentativeAccounts???
                        if (Key.PROP_KEY_OWNER_REPRESENTIVE_ACCOUNTS.equals(keyQName)) {
                            NodeList accountNodeList = element.getElementsByTagNameNS(elementNameSpace,
                                    Key.PROP_KEY_OWNER_REPRESENTIVE_ACCOUNT.getLocalPart());
                            for (int i = 0; i < accountNodeList.getLength(); i++) {
                                this.ownerRepresentativeAccounts
                                        .add(accountNodeList.item(i).getTextContent().trim());
                            }
                        }
                    }
                }
            } catch (ParseException e) {
                // ES?JSON???
                throw DcCoreException.Dav.FS_INCONSISTENCY_FOUND.reason(e);
            }
        }
    }

    /**
     * Node???.
     * @return Node??
     */
    public DcGetResponse getNode() {
        DcGetResponse res = this.getEsColType().get(this.nodeId);
        return res;
    }

    private Element parseProp(String value) {
        // valDOM?Element
        DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
        factory.setNamespaceAware(true);
        DocumentBuilder builder = null;
        Document doc = null;
        try {
            builder = factory.newDocumentBuilder();
            ByteArrayInputStream is = new ByteArrayInputStream(value.getBytes(CharEncoding.UTF_8));
            doc = builder.parse(is);
        } catch (Exception e1) {
            throw DcCoreException.Dav.DAV_INCONSISTENCY_FOUND.reason(e1);
        }
        Element e = doc.getDocumentElement();
        return e;
    }

    /**
     * ????EsType????.
     * @return EsType
     */
    public DavNodeAccessor getEsColType() {
        return this.parent.getEsColType();
    }

    @SuppressWarnings("unchecked")
    @Override
    public final Multistatus propfind(final Propfind propfind, final String depth, final String url,
            final boolean isAclRead) {
        String reqUri = url;

        // Depth??? 0, 1
        // infinity?????????403??
        if ("infinity".equals(depth)) {
            throw DcCoreException.Dav.PROPFIND_FINITE_DEPTH;
        } else if (depth == null) {
            throw DcCoreException.Dav.INVALID_DEPTH_HEADER.params("null");
        } else if (!("0".equals(depth) || "1".equals(depth))) {
            throw DcCoreException.Dav.INVALID_DEPTH_HEADER.params(depth);
        }

        // reqUri = currentUrl.getPath();
        // ?/???????????
        if (reqUri.endsWith("/")) {
            reqUri = reqUri.substring(0, reqUri.length() - 1);
        }
        String[] paths = reqUri.split("/");
        String nm = "";
        if (paths.length > 0) {
            nm = paths[paths.length - 1];
        }

        Multistatus res = this.of.createMultistatus();

        // ????????URL?
        int resourcePos = reqUri.lastIndexOf("/");
        if (resourcePos != -1) {
            String resourceName = reqUri.substring(resourcePos + 1);
            try {
                resourceName = URLEncoder.encode(resourceName, "UTF-8");
            } catch (UnsupportedEncodingException e) {
                log.debug("UnsupportedEncodingException");
            }
            String collectionUrl = reqUri.substring(0, resourcePos);
            reqUri = collectionUrl + "/" + resourceName;
        }

        List<org.apache.wink.webdav.model.Response> resps = res.getResponse();
        org.apache.wink.webdav.model.Response r0 = this.createDavResponse(nm, reqUri, this.davNode.getSource(),
                propfind, isAclRead);
        resps.add(r0);

        // Depth ?0?????
        if ("0".equals(depth)) {
            return res;
        }

        DcSearchResponse resp = getChildResource();
        if (resp == null) {
            return res;
        }

        DcSearchHit[] hits = resp.getHits().getHits();

        if (hits.length == 0) {
            return res;
        }

        // ??nodeId??
        final Map<String, Map<String, Object>> mapJson = new HashMap<String, Map<String, Object>>();
        for (DcSearchHit hit : hits) {
            String id = hit.getId();
            Map<String, Object> childJson = hit.getSource();
            mapJson.put(id, childJson);
        }

        // Response
        Iterator<String> itr = this.davNode.getChildren().keySet().iterator();
        while (itr.hasNext()) {
            String childName = itr.next();
            String childNodeId = this.davNode.getChildren().get(childName);
            Map<String, Object> childJson = mapJson.get(childNodeId);

            try {
                childName = URLEncoder.encode(childName, "UTF-8");
            } catch (UnsupportedEncodingException e) {
                log.debug("UnsupportedEncodingException:" + childName);
            }
            org.apache.wink.webdav.model.Response rs = this.createDavResponse(childName, reqUri + "/" + childName,
                    childJson, propfind, isAclRead);
            resps.add(rs);

        }

        return res;
    }

    /**
     * ????.
     * @return ???
     */
    public DcSearchResponse getChildResource() {
        // ????
        Map<String, Object> source = new HashMap<String, Object>();

        // ?????????
        List<Map<String, Object>> implicitFilters = QueryMapFactory.getImplicitFilters(this.cell.getId(),
                this.box.getId(), null, null, this.getEsColType().getType());
        implicitFilters.add(0, QueryMapFactory.termQuery(DavNode.KEY_PARENT, this.nodeId));
        Map<String, Object> query = QueryMapFactory.mustQuery(implicitFilters);
        Map<String, Object> filteredQuery = QueryMapFactory.filteredQuery(null, query);
        source.put("query", filteredQuery);
        // ?
        // Dav???????????????????
        // TODO ??????????????????????????
        // ?????????????
        source.put("size", TOP_NUM);

        DcSearchResponse resp = this.getEsColType().search(source);
        return resp;
    }

    /*
     * proppatch ??. ??? key = namespaceUri + "@" + localName Value = inner XML String
     */
    @Override
    @SuppressWarnings("unchecked")
    public Multistatus proppatch(final Propertyupdate propUpdate, final String url) {
        long now = new Date().getTime();
        String reqUri = url;
        Multistatus ms = this.of.createMultistatus();
        Response res = this.of.createResponse();
        res.getHref().add(reqUri);

        // 
        Lock lock = this.lock();
        // ?
        try {
            this.load(); // ??
            Map<String, Object> propsJson = (Map<String, Object>) this.davNode.getProperties();

            List<Prop> propsToSet = propUpdate.getPropsToSet();

            for (Prop prop : propsToSet) {
                if (null == prop) {
                    throw DcCoreException.Dav.XML_CONTENT_ERROR;
                }
                List<Element> lpe = prop.getAny();
                for (Element elem : lpe) {
                    res.setProperty(elem, HttpStatus.SC_OK);
                    String key = elem.getLocalName() + "@" + elem.getNamespaceURI();
                    String value = DcCoreUtils.nodeToString(elem);
                    log.debug("key: " + key);
                    log.debug("val: " + value);
                    propsJson.put(key, value);
                }
            }

            List<Prop> propsToRemove = propUpdate.getPropsToRemove();
            for (Prop prop : propsToRemove) {
                if (null == prop) {
                    throw DcCoreException.Dav.XML_CONTENT_ERROR;
                }
                List<Element> lpe = prop.getAny();
                for (Element elem : lpe) {

                    String key = elem.getLocalName() + "@" + elem.getNamespaceURI();
                    String v = (String) propsJson.get(key);
                    log.debug("Removing key: " + key);
                    if (v == null) {
                        res.setProperty(elem, HttpStatus.SC_NOT_FOUND);
                    } else {
                        propsJson.remove(key);
                        res.setProperty(elem, HttpStatus.SC_OK);
                    }
                }
            }
            // 
            this.davNode.setUpdated(now);
            // JSON???
            setPropToJson(propsJson);
            DcIndexResponse resp = updateNodeWithVersion();
            // ETAG??Version??
            this.version = resp.version();
        } finally {
            // 
            lock.release();
        }
        ms.getResponse().add(res);
        return ms;
    }

    /**
     * json?Prop?.
     * @param propsJson Prop
     */
    protected void setPropToJson(Map<String, Object> propsJson) {
    }

    /**
     * ??Node??.
     * @return ?.
     */
    public DcIndexResponse updateNodeWithVersion() {
        DcIndexResponse resp;
        resp = this.getEsColType().update(this.nodeId, this.davNode, this.version);
        return resp;
    }

    /**
     * ??Node??.
     * @return ?.
     */
    public DcIndexResponse updateNodeWithVersionForFile() {
        DcIndexResponse resp;
        resp = this.getEsColType().updateForFile(this.nodeId, this.davNode, this.version);
        return resp;
    }

    @Override
    @SuppressWarnings("unchecked")
    public final ResponseBuilder acl(final Reader reader) {
        // ???????????
        Acl aclToSet = null;
        try {
            aclToSet = ObjectIo.unmarshal(reader, Acl.class);
        } catch (Exception e1) {
            throw DcCoreException.Dav.XML_CONTENT_ERROR.reason(e1);
        }
        if (!aclToSet.validateAcl(isCellLevel())) {
            throw DcCoreException.Dav.XML_VALIDATE_ERROR;
        }
        JSONParser parser = new JSONParser();
        JSONObject aclJson = null;
        try {
            aclJson = (JSONObject) parser.parse(aclToSet.toJSON());
        } catch (ParseException e) {
            throw DcCoreException.Dav.XML_ERROR.reason(e);
        }
        // 
        Lock lock = this.lock();
        try {
            // ACL?xml:base???
            Object objAclBase = aclJson.get(KEY_ACL_BASE);
            String aclBase = null;
            if (objAclBase != null) {
                aclBase = (String) objAclBase;
            }

            // principal?href ? ??Name  ID__id ???
            Object jsonObj = aclJson.get(KEY_ACE);
            JSONArray array = new JSONArray();
            if (jsonObj instanceof JSONObject) {
                array.add(jsonObj);
            } else {
                array = (JSONArray) jsonObj;
            }
            if (array != null) {
                for (Object ace : (JSONArray) array) {
                    JSONObject aceJson = (JSONObject) ace;
                    JSONObject principal = (JSONObject) aceJson.get(KEY_ACL_PRINCIPAL);
                    if (principal.get(KEY_ACL_HREF) != null) {
                        principal.put(KEY_ACL_HREF,
                                roleResourceUrlToId((String) principal.get(KEY_ACL_HREF), aclBase));
                    } else if (principal.get(KEY_ACL_ALL) != null) {
                        principal.put(KEY_ACL_ALL, null);
                    }

                }
            }
            // ES?xm:base????
            aclJson.remove(KEY_ACL_BASE);
            setAclToJson(aclJson);
            // ????
            DcIndexResponse resp = updateNode();
            this.version = resp.getVersion();
            this.acl = aclToSet;
            // ?
            return javax.ws.rs.core.Response.status(HttpStatus.SC_OK).header(HttpHeaders.ETAG, this.getEtag());
        } finally {
            lock.release();
        }
    }

    /**
     * json?ACL?.
     * @param aclJson ACL
     */
    protected void setAclToJson(JSONObject aclJson) {
        this.davNode.setAcl(aclJson);
    }

    /**
     * Node??.
     * @return ?.
     */
    public DcIndexResponse updateNode() {
        DcIndexResponse resp;
        resp = this.getEsColType().update(this.nodeId, this.davNode);
        return resp;
    }

    @Override
    public final ResponseBuilder putForCreate(final String contentType, final InputStream inputStream) {
        // ?

        long now = new Date().getTime();
        // creating node Document
        DavNode fileNode = new DavNode(this.cell.getId(), this.box.getId(), DavCmp.TYPE_DAV_FILE);
        fileNode.setParentId(this.parent.nodeId);

        // ?
        Map<String, Object> data = new HashMap<String, Object>();
        fileNode.setFile(data);
        data.put(KEY_CONTENT_TYPE, contentType);

        BufferedInputStream bufferedInput = new BufferedInputStream(inputStream);

        // 
        Lock lock = this.lock();
        try {
            // ????????
            // ???????????????
            this.parent.load();
            this.nodeId = this.parent.davNode.getChildren().get(this.name);
            if (this.nodeId != null) {
                this.load();
                // ????
                return this.doPutForUpdate(contentType, inputStream, null);
            }

            // ???
            checkChildResourceCount();

            String newId = DcUUID.randomUUID();
            try {
                BinaryDataAccessor accessor = getBinaryDataAccessor();
                long writtenBytes = accessor.create(bufferedInput, newId);
                data.put(KEY_CONTENT_LENGTH, writtenBytes);
            } catch (BinaryDataNotFoundException nex) {
                throw DcCoreException.Dav.RESOURCE_NOT_FOUND.reason(nex);
            } catch (BinaryDataAccessException ex) {
                throw DcCoreException.Dav.FS_INCONSISTENCY_FOUND.reason(ex);
            }

            // ???.
            this.davNode = fileNode;
            DcActionResponse res = createNodeWithId(newId);
            if (res instanceof DcIndexResponse) {
                this.nodeId = ((DcIndexResponse) res).getId();
                this.version = ((DcIndexResponse) res).version();
            } else if (res instanceof DcGetResponse) {
                this.nodeId = ((DcGetResponse) res).getId();
                this.version = ((DcGetResponse) res).version();
            }

            // adding newNode to this nodeDocument;
            this.parent.linkChild(this.name, this.nodeId, now);
        } finally {
            // UNLOCK
            lock.release();
            log.debug("unlock1");
        }

        return javax.ws.rs.core.Response.ok().status(HttpStatus.SC_CREATED).header(HttpHeaders.ETAG,
                this.getEtag());
    }

    /**
     * Node??.
     * @return ??
     */
    public DcActionResponse createNode() {
        DcIndexResponse res = null;
        String id = DcUUID.randomUUID();
        res = this.getEsColType().create(id, this.davNode);
        return res;
    }

    /**
     * ID??Node??.
     * @param id ID
     * @return ??
     */
    public DcActionResponse createNodeWithId(String id) {
        DcActionResponse res;
        res = this.getEsColType().createForFile(id, this.davNode);
        return res;
    }

    @Override
    public final ResponseBuilder putForUpdate(final String contentType, final InputStream inputStream,
            String etag) {
        // 
        Lock lock = this.lock();
        try {
            return this.doPutForUpdate(contentType, inputStream, etag);
        } finally {
            // ?
            lock.release();
            log.debug("unlock2");
        }
    }

    final ResponseBuilder doPutForUpdate(final String contentType, final InputStream inputStream, String etag) {
        // ??
        long now = new Date().getTime();
        // 
        // TODO ??????????????
        this.load();

        // etag????????*??????????????
        if (etag != null && !"*".equals(etag) && !this.getEtag().equals(etag)) {
            throw DcCoreException.Dav.ETAG_NOT_MATCH;
        }

        // ??
        this.davNode.setUpdated(now);
        Map<String, Object> data = new HashMap<String, Object>();

        data.put(KEY_CONTENT_TYPE, contentType);

        BufferedInputStream bufferedInput = new BufferedInputStream(inputStream);

        try {
            BinaryDataAccessor accessor = getBinaryDataAccessor();
            long writtenBytes = accessor.update(bufferedInput, this.nodeId);
            data.put(KEY_CONTENT_LENGTH, writtenBytes);
        } catch (BinaryDataNotFoundException nex) {
            throw DcCoreException.Dav.RESOURCE_NOT_FOUND.reason(nex);
        } catch (BinaryDataAccessException ex) {
            throw DcCoreException.Dav.FS_INCONSISTENCY_FOUND.reason(ex);
        }

        this.davNode.setFile(data);
        // ????
        DcIndexResponse res = updateNodeWithVersionForFile();
        this.version = res.getVersion();
        return javax.ws.rs.core.Response.ok().status(HttpStatus.SC_NO_CONTENT).header(HttpHeaders.ETAG,
                this.getEtag());

    }

    @Override
    public final ResponseBuilder get(final String ifNoneMatch, final String rangeHeaderField) {
        String storedEtag = this.getEtag();
        // ifNoneMatch????? Not-Modified?.
        if (storedEtag.equals(ifNoneMatch)) {
            return javax.ws.rs.core.Response.notModified().header(HttpHeaders.ETAG, storedEtag);
        }

        Map<String, Object> data = (Map<String, Object>) this.davNode.getFile();
        String contentType = (String) data.get(KEY_CONTENT_TYPE);

        BinaryDataAccessor accessor = getBinaryDataAccessor();
        ResponseBuilder res = null;
        final long fileSize = accessor.getSize(this.nodeId);

        // Range??
        final RangeHeaderHandler range = RangeHeaderHandler.parse(rangeHeaderField, fileSize);

        try {
            String fileFullPath = accessor.getFilePath(this.nodeId);

            // Range??????
            if (!range.isValid()) {
                // ?
                StreamingOutput sout = new StreamingOutputForDavFile(fileFullPath);
                res = davFileResponse(sout, fileSize, contentType);
            } else {
                // Range?

                // Range??
                if (!range.isSatisfiable()) {
                    DcCoreLog.Dav.REQUESTED_RANGE_NOT_SATISFIABLE.params(range.getRangeHeaderField()).writeLog();
                    throw DcCoreException.Dav.REQUESTED_RANGE_NOT_SATISFIABLE;
                }

                if (range.getByteRangeSpecCount() > 1) {
                    // MultiPart???
                    throw DcCoreException.Misc.NOT_IMPLEMENTED.params("Range-MultiPart");
                } else {
                    StreamingOutput sout = new StreamingOutputForDavFileWithRange(fileFullPath, fileSize, range);
                    res = davFileResponseForRange(sout, fileSize, contentType, range);
                }
            }
            return res.header(HttpHeaders.ETAG, this.getEtag()).header(DcCoreUtils.HttpHeaders.ACCEPT_RANGES,
                    RangeHeaderHandler.BYTES_UNIT);

        } catch (BinaryDataNotFoundException nex) {
            throw DcCoreException.Dav.DAV_UNAVAILABLE.reason(nex);
        }
    }

    /**
     * ??.
     * @param sout StreamingOuput
     * @param fileSize 
     * @param contentType 
     * @return ?
     */
    public ResponseBuilder davFileResponse(final StreamingOutput sout, long fileSize, String contentType) {
        return javax.ws.rs.core.Response.ok(sout).header(HttpHeaders.CONTENT_LENGTH, fileSize)
                .header(HttpHeaders.CONTENT_TYPE, contentType);
    }

    /**
     * ??.
     * @param sout StreamingOuput
     * @param fileSize 
     * @param contentType 
     * @param range RangeHeaderHandler
     * @return ?
     */
    public ResponseBuilder davFileResponseForRange(final StreamingOutput sout, long fileSize, String contentType,
            final RangeHeaderHandler range) {
        // MultiPart???????1?byte-renge-set????
        int rangeIndex = 0;
        List<ByteRangeSpec> brss = range.getByteRangeSpecList();
        final ByteRangeSpec brs = brss.get(rangeIndex);

        // iPad?safari????Chunked?Range????????????Content-Length????
        return javax.ws.rs.core.Response.status(HttpStatus.SC_PARTIAL_CONTENT).entity(sout)
                .header(DcCoreUtils.HttpHeaders.CONTENT_RANGE, brs.makeContentRangeHeaderField())
                .header(HttpHeaders.CONTENT_LENGTH, brs.getContentLength())
                .header(HttpHeaders.CONTENT_TYPE, contentType);
    }

    @Override
    public final String getName() {
        return this.name;
    }

    @Override
    public final DavCmp getChild(final String childName) {
        if (this.davNode == null || this.davNode.getChildren() == null) {
            return new DavCmpEsImpl(childName, this, this.cell, this.box, null);
        }
        String childNodeId = this.davNode.getChildren().get(childName);
        if (childNodeId == null) {
            return new DavCmpEsImpl(childName, this, this.cell, this.box, null);
        }
        return new DavCmpEsImpl(childName, this, this.cell, this.box, childNodeId);
    }

    @Override
    public final String getType() {
        if (this.davNode == null) {
            return DavCmp.TYPE_NULL;
        }
        return (String) this.davNode.getNodeType();
    }

    @SuppressWarnings({ "unchecked" })
    final org.apache.wink.webdav.model.Response createDavResponse(final String pathName, final String href,
            final Map<String, Object> nodeJson, final Propfind propfind, final boolean isAclRead) {
        org.apache.wink.webdav.model.Response ret = this.of.createResponse();
        ret.getHref().add(href);

        // TODO v1.1 PROPFIND???????
        if (propfind != null) {

            log.debug("isAllProp:" + propfind.isAllprop());
            log.debug("isPropName:" + propfind.isPropname());
        } else {
            log.debug("propfind is null");
        }

        /*
         * Displayname dn = of.createDisplayname(); dn.setValue(name); ret.setPropertyOk(dn);
         */

        Long updated = (Long) nodeJson.get(DavNode.KEY_UPDATED);
        if (updated != null) {
            Getlastmodified lm = of.createGetlastmodified();
            lm.setValue(new Date(updated));
            ret.setPropertyOk(lm);
        }
        Long published = (Long) nodeJson.get(DavNode.KEY_PUBLISHED);
        if (published != null) {
            Creationdate cd = of.createCreationdate();
            cd.setValue(new Date(published));
            ret.setPropertyOk(cd);
        }
        if (DavCmp.TYPE_DAV_FILE.equals(nodeJson.get(DavNode.KEY_NODE_TYPE))) {
            // Dav ?????
            Resourcetype rt1 = of.createResourcetype();
            ret.setPropertyOk(rt1);
            // JSONObject atmts = (JSONObject) nodeJson.get("_attachments");
            Map<String, Object> data = (Map<String, Object>) nodeJson.get(DavNode.KEY_FILE);
            // JSONObject atmt = (JSONObject) atmts.get("attachment");
            // Long contentlength = (Long) atmt.get("length");
            Getcontentlength gcl = new Getcontentlength();
            gcl.setValue(String.valueOf(data.get(KEY_CONTENT_LENGTH)));
            ret.setPropertyOk(gcl);
            String contentType = (String) data.get(KEY_CONTENT_TYPE);
            Getcontenttype gct = new Getcontenttype();
            gct.setValue(contentType);
            ret.setPropertyOk(gct);
        } else if (DavCmp.TYPE_COL_ODATA.equals(nodeJson.get(DavNode.KEY_NODE_TYPE))) {
            // OData ?????
            Resourcetype colRt = of.createResourcetype();
            colRt.setCollection(of.createCollection());
            List<Element> listElement = colRt.getAny();
            QName qname = new QName(DcCoreUtils.XmlConst.NS_DC1, DcCoreUtils.XmlConst.ODATA,
                    DcCoreUtils.XmlConst.NS_PREFIX_DC1);
            Element element = WebDAVModelHelper.createElement(qname);
            listElement.add(element);
            ret.setPropertyOk(colRt);

        } else if (DavCmp.TYPE_COL_SVC.equals(nodeJson.get(DavNode.KEY_NODE_TYPE))) {
            // Service ?????
            Resourcetype colRt = of.createResourcetype();
            colRt.setCollection(of.createCollection());
            List<Element> listElement = colRt.getAny();
            QName qname = new QName(DcCoreUtils.XmlConst.NS_DC1, DcCoreUtils.XmlConst.SERVICE,
                    DcCoreUtils.XmlConst.NS_PREFIX_DC1);
            Element element = WebDAVModelHelper.createElement(qname);
            listElement.add(element);
            ret.setPropertyOk(colRt);

        } else {
            // Col ?????
            Resourcetype colRt = of.createResourcetype();
            colRt.setCollection(of.createCollection());
            ret.setPropertyOk(colRt);

        }

        // ACL??
        if (isAclRead) {
            Map<String, Object> aclJson = (Map<String, Object>) nodeJson.get(DavNode.KEY_ACL);
            if (aclJson != null) {
                JSONParser parser = new JSONParser();
                JSONObject aclObj = null;
                try {
                    aclObj = (JSONObject) parser.parse(JSONObject.toJSONString(aclJson));
                } catch (ParseException e2) {
                    throw new WebApplicationException(e2);
                }
                Document aclDoc = null;

                // base:xml?
                String baseUrlStr = createBaseUrlStr();
                // principal?href ? ID__id?URL???
                this.roleIdToName(aclObj.get(KEY_ACE), baseUrlStr);

                final Acl objAcl = Acl.fromJson(aclObj.toJSONString());
                objAcl.setBase(baseUrlStr);
                objAcl.setRequireSchemaAuthz((String) aclObj.get(KEY_REQUIRE_SCHEMA_AUTHZ));
                DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
                dbf.setNamespaceAware(true);
                try {
                    aclDoc = dbf.newDocumentBuilder().newDocument();
                    ObjectIo.marshal(objAcl, aclDoc);
                } catch (Exception e) {
                    throw new WebApplicationException(e);
                }
                if (aclDoc != null) {
                    Element e = aclDoc.getDocumentElement();
                    ret.setPropertyOk(e);
                }
            }
        }
        Map<String, String> props = (Map<String, String>) nodeJson.get(DavNode.KEY_PROPS);
        if (props != null) {
            List<String> nsList = new ArrayList<String>();
            for (Map.Entry<String, String> entry : props.entrySet()) {
                String key = entry.getKey();
                String val = entry.getValue();
                int idx = key.indexOf("@");
                String ns = key.substring(idx + 1, key.length());

                int nsIdx = nsList.indexOf(ns);
                if (nsIdx == -1) {
                    nsList.add(ns);
                }

                Element e = parseProp(val);

                ret.setPropertyOk(e);
            }

        }
        return ret;
    }

    @Override
    public final ResponseBuilder mkcol(final String type) {
        // ???
        this.davNode = new DavNode(this.cell.getId(), this.box.getId(), type);
        this.davNode.setParentId(this.parent.nodeId);

        // 
        Lock lock = this.lock();
        try {
            // ????????
            // ???????????????
            this.parent.load();
            if (this.parent.davNode.getChildren().get(this.name) != null) {
                // ????????
                // ??????EXCEPTION
                throw DcCoreException.Dav.METHOD_NOT_ALLOWED;
            }

            // ???
            DavCmpEsImpl current = this;
            int depth = 0;
            int maxDepth = DcCoreConfig.getMaxCollectionDepth();
            while (null != current.parent) {
                current = current.parent;
                depth++;
            }
            if (depth > maxDepth) {
                // ???????400??
                throw DcCoreException.Dav.COLLECTION_DEPTH_ERROR;
            }

            // ???
            checkChildResourceCount();

            // ???
            DcActionResponse resp = createNode();
            if (resp instanceof DcIndexResponse) {
                this.nodeId = ((DcIndexResponse) resp).getId();
                this.version = ((DcIndexResponse) resp).version();
            } else if (resp instanceof DcGetResponse) {
                this.nodeId = ((DcGetResponse) resp).getId();
                this.version = ((DcGetResponse) resp).version();
            }

            // ??
            this.parent.linkChild(this.name, this.nodeId, this.davNode.getPublished());
        } finally {
            // UNLOCK
            lock.release();
            log.debug("unlock");
        }

        // ?
        return javax.ws.rs.core.Response.status(HttpStatus.SC_CREATED).header(HttpHeaders.ETAG, this.getEtag());
    }

    private void checkChildResourceCount() {
        // ???
        int maxChildResource = DcCoreConfig.getMaxChildResourceCount();
        if (this.parent.getChildrenCount() >= maxChildResource) {
            // ???????????400??
            throw DcCoreException.Dav.COLLECTION_CHILDRESOURCE_ERROR;
        }
    }

    @Override
    public final ResponseBuilder linkChild(final String childName, final String childNodeId, final Long asof) {
        this.davNode.getChildren().put(childName, childNodeId);
        this.davNode.setUpdated(asof);
        // ????
        updateNode();
        return null;
    }

    @Override
    public final ResponseBuilder unlinkChild(final String childName, final Long asof) {
        this.davNode.getChildren().remove(childName);
        this.davNode.setUpdated(asof);

        // ????
        updateNode();
        return null;
    }

    /**
     * ?.
     * @param ifMatch ifMatch
     * @return JaxRS
     */
    @Override
    public final ResponseBuilder delete(final String ifMatch) {
        // etag????????*??????????????
        if (ifMatch != null && !"*".equals(ifMatch) && !this.getEtag().equals(ifMatch)) {
            throw DcCoreException.Dav.ETAG_NOT_MATCH;
        }
        long now = new Date().getTime();
        // 
        Lock lock = this.lock();
        try {
            // WebDAV????????????
            if (TYPE_COL_WEBDAV.equals(this.getType()) && this.davNode.getChildren().size() > 0) {
                throw DcCoreException.Dav.HAS_CHILDREN;
            }

            if (this.parent != null) {
                // ????????
                // ???????????????
                this.parent.load();
                if (this.parent.davNode.getChildren().get(this.name) == null) {
                    // ????????????
                    // ????????EXCEPTION
                    throw DcCoreException.Dav.RESOURCE_NOT_FOUND;
                }
                // Service?????__src??
                //  ????? DavCmp ???????????500?????
                if (TYPE_COL_SVC.equals(this.getType())) {
                    DavCmp srcCmp = this.getChild(DavCmp.SERVICE_SRC_COLLECTION);
                    if (srcCmp instanceof DavCmpEsImpl) {
                        ((DavCmpEsImpl) srcCmp).deleteNode();
                    } else {
                        throw DcCoreException.Dav.DAV_INCONSISTENCY_FOUND;
                    }
                }
                this.parent.unlinkChild(this.name, now);
            }
            deleteNode();
        } finally {
            // LOCK
            log.debug("unlcok");
            lock.release();
        }
        return javax.ws.rs.core.Response.ok().status(HttpStatus.SC_NO_CONTENT);
    }

    /**
     * Node?.
     */
    public void deleteNode() {
        deleteNode(this.nodeId);
    }

    /**
     * Nodeid?Node?.
     * @param deleteNodeId NodeId
     */
    public void deleteNode(final String deleteNodeId) {
        this.getEsColType().delete(this.davNode);

        BinaryDataAccessor accessor = getBinaryDataAccessor();
        try {
            accessor.delete(deleteNodeId);
        } catch (BinaryDataAccessException e) {
            throw DcCoreException.Dav.FS_INCONSISTENCY_FOUND.reason(e);
        }
    }

    /**
     * ???????.
     * @return ?
     */
    protected BinaryDataAccessor getBinaryDataAccessor() {
        String owner = cell.getOwner();
        String unitUserName = null;
        if (owner == null) {
            unitUserName = AccessContext.TYPE_ANONYMOUS;
        } else {
            unitUserName = IndexNameEncoder.encodeEsIndexName(owner);
        }

        return new BinaryDataAccessor(DcCoreConfig.getBlobStoreRoot(), unitUserName,
                DcCoreConfig.getPhysicalDeleteMode(), DcCoreConfig.getFsyncEnabled());
    }

    @Override
    public final DavCmp getParent() {
        return this.parent;
    }

    /**
     * BoxId??.
     * @return boxId
     */
    public final String getBoxId() {
        return this.box.getId();
    }

    /**
     * nodeId??.
     * @return nodeId
     */
    public final String getNodeId() {
        return this.nodeId;
    }

    @Override
    public final DcODataProducer getODataProducer() {
        return ModelFactory.ODataCtl.userData(this.cell, this);
    }

    @Override
    public final DcODataProducer getSchemaODataProducer(Cell cellObject) {
        return ModelFactory.ODataCtl.userSchema(cellObject, this);
    }

    @Override
    public final int getChildrenCount() {
        return this.davNode.getChildren().keySet().size();
    }

    private String roleResourceUrlToId(String roleUrl, String baseUrl) {
        EntitySetAccessor roleType = EsModel.cellCtl(this.cell, Role.EDM_TYPE_NAME);

        // roleName?URL?
        URL url = null;
        try {
            // xml:base?
            if (baseUrl != null && !"".equals(baseUrl)) {
                // URL?
                url = new URL(new URL(baseUrl), roleUrl);
            } else {
                url = new URL(roleUrl);
            }
        } catch (MalformedURLException e) {
            throw DcCoreException.Dav.ROLE_NOT_FOUND.reason(e);
        }

        Role role = null;
        try {
            role = new Role(url);
        } catch (MalformedURLException e) {
            log.info("Role URL:" + url.toString());
            throw DcCoreException.Dav.ROLE_NOT_FOUND;
        }

        // ?URL?ACL?URL??????????
        if (!(this.cell.getUrl().equals(role.getBaseUrl()))) {
            DcCoreLog.Dav.ROLE_NOT_FOUND.params("Cell different").writeLog();
            throw DcCoreException.Dav.ROLE_NOT_FOUND;
        }
        // Role?
        List<Map<String, Object>> queries = new ArrayList<Map<String, Object>>();
        queries.add(QueryMapFactory.termQuery("c", this.cell.getId()));
        queries.add(QueryMapFactory.termQuery("s." + KEY_NAME + ".untouched", role.getName()));

        Map<String, Object> query = QueryMapFactory.filteredQuery(null, QueryMapFactory.mustQuery(queries));

        List<Map<String, Object>> filters = new ArrayList<Map<String, Object>>();
        if (!(Box.DEFAULT_BOX_NAME.equals(role.getBoxName()))) {
            // Role????????
            Box targetBox = this.cell.getBoxForName(role.getBoxName());
            if (targetBox == null) {
                throw DcCoreException.Dav.BOX_LINKED_BY_ROLE_NOT_FOUND.params(baseUrl);
            }
            String boxId = targetBox.getId();
            filters.add(QueryMapFactory.termQuery(KEY_LINK + "." + Box.EDM_TYPE_NAME, boxId));
        } else {
            // Role????????null
            filters.add(QueryMapFactory.missingFilter(KEY_LINK + "." + Box.EDM_TYPE_NAME));
        }

        Map<String, Object> source = new HashMap<String, Object>();
        if (!filters.isEmpty()) {
            source.put("filter", QueryMapFactory.andFilter(filters));
        }
        source.put("query", query);
        DcSearchHits hits = roleType.search(source).getHits();

        // ?Role??????Null
        if (hits == null || hits.getCount() == 0) {
            DcCoreLog.Dav.ROLE_NOT_FOUND.params("Not Hit").writeLog();
            throw DcCoreException.Dav.ROLE_NOT_FOUND;
        }
        // ?Role????????
        if (hits.getAllPages() > 1) {
            DcCoreLog.OData.FOUND_MULTIPLE_RECORDS.params(hits.getAllPages()).writeLog();
            throw DcCoreException.OData.DETECTED_INTERNAL_DATA_CONFLICT;
        }

        DcSearchHit hit = hits.getHits()[0];
        return hit.getId();
    }

    /**
     * ID?URL?. jsonObj?IDURL???
     * @param jsonObj ID??JSON
     * @param baseUrlStr xml:base
     */
    @SuppressWarnings("unchecked")
    private void roleIdToName(Object jsonObj, String baseUrlStr) {

        JSONArray array = new JSONArray();
        if (jsonObj instanceof JSONObject) {
            array.add(jsonObj);
        } else {
            array = (JSONArray) jsonObj;
        }
        if (array != null) {
            // xml:base
            for (int i = 0; i < array.size(); i++) {
                JSONObject aceJson = (JSONObject) array.get(i);
                JSONObject principal = (JSONObject) aceJson.get(KEY_ACL_PRINCIPAL);
                if (principal.get(KEY_ACL_HREF) != null) {
                    // ID????????????????
                    String roloResourceUrl = roleIdToRoleResourceUrl((String) principal.get(KEY_ACL_HREF));
                    if (roloResourceUrl == null) {
                        // ID???????????????ACE???
                        array.remove(i);
                        --i;
                        // ????ID??????ACE??
                        if (array.isEmpty() && jsonObj instanceof JSONObject) {
                            JSONObject objJson = (JSONObject) jsonObj;
                            objJson.clear();
                        }
                        continue;
                    }
                    // base:xml?URL?
                    roloResourceUrl = baseUrlToRoleResourceUrl(baseUrlStr, roloResourceUrl);
                    principal.put(KEY_ACL_HREF, roloResourceUrl);
                } else if (principal.get(KEY_ACL_ALL) != null) {
                    principal.put(KEY_ACL_ALL, null);
                }
            }
        }
    }

    /**
     * ID????.
     * @param roleId ID
     * @return ??
     */
    @SuppressWarnings("unchecked")
    private String roleIdToRoleResourceUrl(String roleId) {
        String boxName = null;
        String schema = null;

        EntitySetAccessor roleType = EsModel.cellCtl(this.cell, Role.EDM_TYPE_NAME);
        DcGetResponse hit = roleType.get(roleId);

        if (hit == null || !hit.isExists()) {
            // ??????null?
            return null;
        }
        Map<String, Object> role = hit.getSource();
        Map<String, Object> s = (Map<String, Object>) role.get(DavNode.KEY_PARENT);
        Map<String, Object> l = (Map<String, Object>) role.get(KEY_LINK);
        String roleName = (String) s.get(KEY_NAME);
        String boxId = (String) l.get(Box.EDM_TYPE_NAME);
        if (boxId != null) {
            // Box?
            Map<String, Object> boxsrc = searchBox(this.cell, boxId);
            Map<String, Object> boxs = (Map<String, Object>) boxsrc.get("s");
            boxName = (String) boxs.get(KEY_NAME);
            schema = (String) boxs.get(KEY_SCHEMA);
        }
        Role roleObj = new Role(roleName, boxName, schema, this.cell.getUrl());
        return roleObj.createUrl();
    }

    /**
     * PROPFIND?ACL?xml:base????.
     * @return
     */
    private String createBaseUrlStr() {
        String result = null;
        if (this.box != null) {
            // Box?ACL???Box?URL
            // URL?????????URL??????
            result = String.format(Role.ROLE_RESOURCE_FORMAT, this.cell.getUrl().replaceFirst("/$", ""),
                    this.box.getName(), "");
        } else {
            // Cell?ACL???Box?URL
            // URL?????????URL??????
            result = String.format(Role.ROLE_RESOURCE_FORMAT, this.cell.getUrl().replaceFirst("/$", ""),
                    Box.DEFAULT_BOX_NAME, "");
        }
        return result;
    }

    /**
     * xml:base???RoleResorceUrl?.
     * @param baseUrlStr xml:base?
     * @param roloResourceUrl URL
     * @return
     */
    private String baseUrlToRoleResourceUrl(String baseUrlStr, String roloResourceUrlStr) {
        String result = null;
        Role baseUrl = null;
        Role roloResourceUrl = null;
        try {
            // base:xml?URL????????__?
            baseUrl = new Role(new URL(baseUrlStr + "__"));
            roloResourceUrl = new Role(new URL(roloResourceUrlStr));
        } catch (MalformedURLException e) {
            throw DcCoreException.Dav.ROLE_NOT_FOUND.reason(e);
        }
        if (baseUrl.getBoxName().equals(roloResourceUrl.getBoxName())) {
            // base:xml?BOX?URL?BOX????
            result = roloResourceUrl.getName();
        } else {
            // base:xml?BOX?URL?BOX???
            result = String.format(ACL_RELATIVE_PATH_FORMAT, roloResourceUrl.getBoxName(),
                    roloResourceUrl.getName());
        }
        return result;
    }

    static final String KEY_LINK = "l";
    static final String KEY_CONTENT_TYPE = "ct";
    static final String KEY_CONTENT_LENGTH = "length";
    static final String KEY_BASE64 = "b64";
    static final String KEY_NAME = "Name";
    static final String KEY_SCHEMA = "Schema";
    static final String KEY_ACL_PRINCIPAL = "D.principal";
    static final String KEY_ACE = "D.ace";
    static final String KEY_ACL_HREF = "D.href";
    static final String KEY_ACL_ALL = "D.all";
    static final String KEY_ACL_BASE = "@base";
    static final String KEY_REQUIRE_SCHEMA_AUTHZ = "@requireSchemaAuthz";
    static final String ACL_RELATIVE_PATH_FORMAT = "../%s/%s";

    @Override
    public String getId() {
        return this.nodeId;
    }

    /**
     * ID?.
     * @param paramNodeId ID
     */
    public void setId(String paramNodeId) {
        this.nodeId = paramNodeId;
    }

    /**
     * @return cell id
     */
    public String getCellId() {
        return this.cell.getId();
    }

    /**
     * DavNode?.
     * @return DavNode
     */
    public DavNode getDavNode() {
        return this.davNode;
    }

    @Override
    public Long getVersion() {
        return this.version;
    }

    @SuppressWarnings("unchecked")
    @Override
    public Map<String, Object> getSource() {
        return this.davNode.getSource();
    }

    /**
     * BoxId?Es?.
     * @param cellObj Cell
     * @param boxId Id
     * @return ?
     */
    public static Map<String, Object> searchBox(final Cell cellObj, final String boxId) {

        EntitySetAccessor boxType = EsModel.box(cellObj);
        DcGetResponse getRes = boxType.get(boxId);
        if (getRes == null || !getRes.isExists()) {
            DcCoreLog.Dav.ROLE_NOT_FOUND.params("Box Id Not Hit").writeLog();

            throw DcCoreException.Dav.ROLE_NOT_FOUND;
        }
        return getRes.getSource();
    }
}