io.personium.core.model.impl.es.CellEsImpl.java Source code

Java tutorial

Introduction

Here is the source code for io.personium.core.model.impl.es.CellEsImpl.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.model.impl.es;

import java.net.MalformedURLException;
import java.net.URI;
import java.net.URL;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import javax.ws.rs.core.UriBuilder;
import javax.ws.rs.core.UriInfo;

import org.apache.commons.lang.StringUtils;
import org.odata4j.core.OEntity;
import org.odata4j.core.OEntityKey;
import org.odata4j.core.OProperty;
import org.odata4j.expression.BoolCommonExpression;
import org.odata4j.producer.EntitiesResponse;
import org.odata4j.producer.EntityResponse;
import org.odata4j.producer.InlineCount;
import org.odata4j.producer.ODataProducer;
import org.odata4j.producer.QueryInfo;
import org.odata4j.producer.resources.OptionsQueryParser;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import io.personium.common.auth.token.IExtRoleContainingToken;
import io.personium.common.auth.token.Role;
import io.personium.common.es.response.PersoniumGetResponse;
import io.personium.common.es.response.PersoniumSearchHit;
import io.personium.common.es.response.PersoniumSearchHits;
import io.personium.common.es.response.PersoniumSearchResponse;
import io.personium.common.es.util.IndexNameEncoder;
import io.personium.core.PersoniumCoreException;
import io.personium.core.PersoniumCoreLog;
import io.personium.core.PersoniumUnitConfig;
import io.personium.core.auth.AccessContext;
import io.personium.core.auth.AuthUtils;
import io.personium.core.event.EventBus;
import io.personium.core.event.EventUtils;
import io.personium.core.model.Box;
import io.personium.core.model.BoxCmp;
import io.personium.core.model.Cell;
import io.personium.core.model.ModelFactory;
import io.personium.core.model.ctl.Account;
import io.personium.core.model.ctl.Common;
import io.personium.core.model.ctl.ExtCell;
import io.personium.core.model.ctl.ExtRole;
import io.personium.core.model.ctl.ReceivedMessage;
import io.personium.core.model.ctl.Relation;
import io.personium.core.model.ctl.SentMessage;
import io.personium.core.model.file.BinaryDataAccessException;
import io.personium.core.model.file.BinaryDataAccessor;
import io.personium.core.model.impl.es.accessor.CellAccessor;
import io.personium.core.model.impl.es.accessor.EntitySetAccessor;
import io.personium.core.model.impl.es.accessor.ODataLinkAccessor;
import io.personium.core.model.impl.es.cache.BoxCache;
import io.personium.core.model.impl.es.cache.CellCache;
import io.personium.core.model.impl.es.doc.CellDocHandler;
import io.personium.core.model.impl.es.doc.OEntityDocHandler;
import io.personium.core.model.impl.es.odata.CellCtlODataProducer;
import io.personium.core.model.impl.fs.DavCmpFsImpl;
import io.personium.core.model.lock.CellLockManager;
import io.personium.core.odata.OEntityWrapper;
import io.personium.core.utils.UriUtils;
import net.spy.memcached.internal.CheckedOperationTimeoutException;

/**
 * Cell object implemented using ElasticSearch.
 */
public final class CellEsImpl implements Cell {
    private String id;
    private String name;
    private String url;
    private String owner;
    private Long published;
    private Map<String, Object> json;

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

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

    /**
     * constructor.
     */
    public CellEsImpl() {

    }

    @Override
    public EventBus getEventBus() {
        return new EventBus(this);
    }

    @Override
    public boolean isEmpty() {
        CellCtlODataProducer producer = new CellCtlODataProducer(this);
        // check no box exists.
        QueryInfo queryInfo = new QueryInfo(InlineCount.ALLPAGES, null, null, null, null, null, null, null, null);
        if (producer.getEntitiesCount(Box.EDM_TYPE_NAME, queryInfo).getCount() > 0) {
            return false;
        }

        // check that Main Box is empty
        Box defaultBox = this.getBoxForName(Box.DEFAULT_BOX_NAME);
        BoxCmp defaultBoxCmp = ModelFactory.boxCmp(defaultBox);
        if (!defaultBoxCmp.isEmpty()) {
            return false;
        }

        // check that no Cell Control Object exists
        // TODO ??????Type?c:?uuid?????????
        if (producer.getEntitiesCount(Account.EDM_TYPE_NAME, queryInfo).getCount() > 0
                || producer.getEntitiesCount(Role.EDM_TYPE_NAME, queryInfo).getCount() > 0
                || producer.getEntitiesCount(ExtCell.EDM_TYPE_NAME, queryInfo).getCount() > 0
                || producer.getEntitiesCount(ExtRole.EDM_TYPE_NAME, queryInfo).getCount() > 0
                || producer.getEntitiesCount(Relation.EDM_TYPE_NAME, queryInfo).getCount() > 0
                || producer.getEntitiesCount(SentMessage.EDM_TYPE_NAME, queryInfo).getCount() > 0
                || producer.getEntitiesCount(ReceivedMessage.EDM_TYPE_NAME, queryInfo).getCount() > 0) {
            return false;
        }
        // TODO check EventLog
        return true;
    }

    @Override
    public Box getBoxForName(String boxName) {
        if (Box.DEFAULT_BOX_NAME.equals(boxName)) {
            return new Box(this, null);
        }

        // URl???Box????????Box??????null??
        if (!validatePropertyRegEx(boxName, Common.PATTERN_NAME)) {
            return null;
        }
        // ??Box???
        Box cachedBox = BoxCache.get(boxName, this);
        if (cachedBox != null) {
            return cachedBox;
        }

        Box loadedBox = null;
        try {
            ODataProducer op = ModelFactory.ODataCtl.cellCtl(this);
            EntityResponse er = op.getEntity(Box.EDM_TYPE_NAME, OEntityKey.create(boxName), null);
            loadedBox = new Box(this, er.getEntity());
            BoxCache.cache(loadedBox);
            return loadedBox;
        } catch (RuntimeException e) {
            if (e.getCause() instanceof CheckedOperationTimeoutException) {
                return loadedBox;
            } else {
                return null;
            }
        }
    }

    @Override
    public Box getBoxForSchema(String boxSchema) {
        // ???????
        List<String> boxSchemas = UriUtils.getUrlVariations(this.getUnitUrl(), boxSchema);

        ODataProducer op = ModelFactory.ODataCtl.cellCtl(this);
        for (int i = 0; i < boxSchemas.size(); i++) {
            BoolCommonExpression filter = OptionsQueryParser.parseFilter("Schema eq '" + boxSchemas.get(i) + "'");
            QueryInfo qi = QueryInfo.newBuilder().setFilter(filter).build();
            try {
                EntitiesResponse er = op.getEntities(Box.EDM_TYPE_NAME, qi);
                List<OEntity> entList = er.getEntities();
                if (entList.size() == 1) {
                    return new Box(this, entList.get(0));
                }
                continue;
            } catch (RuntimeException e) {
                return null;
            }
        }
        return null;
    }

    /**
     * @param uriInfo
     *            UriInfo
     * @return Cell  ?Cell????????null
     */
    public static Cell load(final UriInfo uriInfo) {
        URI reqUri = uriInfo.getRequestUri();
        URI baseUri = uriInfo.getBaseUri();

        String rPath = reqUri.getPath();
        String bPath = baseUri.getPath();
        rPath = rPath.substring(bPath.length());
        String[] paths = StringUtils.split(rPath, "/");

        return findCell("s.Name.untouched", paths[0], uriInfo);
    }

    /**
     * @param id
     *            id
     * @param uriInfo
     *            UriInfo
     * @return Cell  ?Cell????????null
     */
    public static Cell load(final String id, final UriInfo uriInfo) {
        log.debug(id);
        EntitySetAccessor esCells = EsModel.cell();
        PersoniumGetResponse resp = esCells.get(id);
        if (resp.exists()) {
            CellEsImpl ret = new CellEsImpl();
            ret.setJson(resp.getSource());
            ret.id = resp.getId();
            if (uriInfo != null) {
                ret.url = getBaseUri(uriInfo, ret.name);
            } else {
                ret.url = "/" + ret.name + "/";
            }
            return ret;
        } else {
            return null;
        }
    }

    private static String getBaseUri(final UriInfo uriInfo, String cellName) {
        // URL???Set
        StringBuilder urlSb = new StringBuilder();
        UriBuilder uriBuilder = uriInfo.getBaseUriBuilder();
        uriBuilder.scheme(PersoniumUnitConfig.getUnitScheme());
        urlSb.append(uriBuilder.build().toASCIIString());
        urlSb.append(cellName);
        urlSb.append("/");
        return urlSb.toString();
    }

    /**
     * ID ??Cell??Cell?Cell??.
     * @param queryKey
     *            Cell??(Cell??)
     * @param queryValue
     *            Cell????
     * @param uriInfo
     *            UriInfo
     * @return Cell  ?Cell??????????queryKey?????null
     */
    public static Cell findCell(String queryKey, String queryValue, UriInfo uriInfo) {
        if (!queryKey.equals("_id") && !queryKey.equals("s.Name.untouched")) {
            return null;
        }
        // URl???Cell?????????Cell??????null??
        if (!validatePropertyRegEx(queryValue, Common.PATTERN_NAME)) {
            return null;
        }

        EntitySetAccessor ecCells = EsModel.cell();
        CellEsImpl ret = new CellEsImpl();

        Map<String, Object> cache = CellCache.get(queryValue);
        if (cache == null) {
            Map<String, Object> source = new HashMap<String, Object>();
            Map<String, Object> filter = new HashMap<String, Object>();
            Map<String, Object> term = new HashMap<String, Object>();

            term.put(queryKey, queryValue);
            filter.put("term", term);
            source.put("query", QueryMapFactory.filteredQuery(null, filter));

            PersoniumSearchResponse resp = ecCells.search(source);
            if (resp == null || resp.getHits().getCount() == 0) {
                return null;
            }
            PersoniumSearchHit hit = resp.getHits().getAt(0);
            ret.setJson(hit.getSource());
            ret.id = hit.getId();

            cache = hit.getSource();
            cache.put("_id", hit.getId());
            try {
                CellCache.cache(queryValue, cache);
            } catch (RuntimeException e) {
                if (e.getCause() instanceof CheckedOperationTimeoutException) {
                    // memcached????????????
                    log.info("Faild to cache Cell info.");
                } else {
                    // ??????????
                    throw PersoniumCoreException.Server.SERVER_CONNECTION_ERROR;
                }
            }
        } else {
            ret.setJson(cache);
            ret.id = (String) cache.get("_id");
        }
        ret.url = getBaseUri(uriInfo, ret.name);
        return ret;
    }

    /**
     * Map????.
     * @param json
     *            ?Map
     */
    @SuppressWarnings({ "unchecked", "rawtypes" })
    public void setJson(Map json) {
        this.json = json;
        if (this.json == null) {
            return;
        }
        Map<String, String> urlJson = (Map<String, String>) json.get("s");
        Map<String, String> hJson = (Map<String, String>) json.get("h");
        this.published = (Long) json.get("p");
        this.name = urlJson.get("Name");
        this.owner = hJson.get("Owner");

    }

    @Override
    public OEntityWrapper getAccount(final String username) {
        ODataProducer op = ModelFactory.ODataCtl.cellCtl(this);
        OEntityKey key = OEntityKey.create(username);
        OEntityWrapper oew = null;
        try {
            EntityResponse resp = op.getEntity("Account", key, null);
            oew = (OEntityWrapper) resp.getEntity();
        } catch (PersoniumCoreException dce) {
            log.debug(dce.getMessage());
        }
        return oew;
    }

    @Override
    public boolean authenticateAccount(final OEntityWrapper oew, final String password) {
        // TODO ?????? ?????ID?????????????
        String cred = null;
        if (oew != null) {
            cred = (String) oew.get("HashedCredential");
        }
        String hCred = AuthUtils.hashPassword(password);
        if (hCred.equals(cred)) {
            return true;
        }

        return false;
    }

    @SuppressWarnings("unchecked")
    @Override
    public List<Role> getRoleListForAccount(final String username) {
        // Account?
        EntitySetAccessor accountType = EsModel.cellCtl(this, Account.EDM_TYPE_NAME);

        List<Map<String, Object>> filters = new ArrayList<Map<String, Object>>();
        filters.add(QueryMapFactory.termQuery("s.Name.untouched", username));

        List<Map<String, Object>> queries = new ArrayList<Map<String, Object>>();
        queries.add(QueryMapFactory.termQuery("c", this.getId()));

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

        Map<String, Object> source = new HashMap<String, Object>();
        source.put("filter", QueryMapFactory.andFilter(filters));
        source.put("query", query);

        PersoniumSearchHits hits = accountType.search(source).getHits();

        if (hits.getCount() == 0) {
            return null;
        }

        PersoniumSearchHit hit = hits.getHits()[0];

        List<Role> ret = new ArrayList<Role>();
        ODataLinkAccessor links = EsModel.cellCtlLink(this);

        // ???????
        List<Map<String, Object>> searchRoleQueries = new ArrayList<Map<String, Object>>();
        searchRoleQueries.add(QueryMapFactory.termQuery("t1", "Account"));
        searchRoleQueries.add(QueryMapFactory.termQuery("t2", "Role"));

        List<Map<String, Object>> searchRoleFilters = new ArrayList<Map<String, Object>>();
        searchRoleFilters.add(QueryMapFactory.termQuery("k1", hit.getId()));
        Map<String, Object> and = new HashMap<String, Object>();
        and.put("filters", searchRoleFilters);
        Map<String, Object> searchRoleFilter = new HashMap<String, Object>();
        searchRoleFilter.put("and", and);

        Map<String, Object> searchRoleSource = new HashMap<String, Object>();
        searchRoleSource.put("filter", searchRoleFilter);
        searchRoleSource.put("query",
                QueryMapFactory.filteredQuery(null, QueryMapFactory.mustQuery(searchRoleQueries)));

        // ?
        searchRoleSource.put("size", TOP_NUM);

        PersoniumSearchResponse res = links.search(searchRoleSource);
        if (res == null) {
            return ret;
        }
        PersoniumSearchHit[] hits2 = res.getHits().getHits();
        for (PersoniumSearchHit hit2 : hits2) {
            Map<String, Object> row = hit2.getSource();
            String role = (String) row.get("k2");
            log.debug(this.id);
            EntitySetAccessor roleDao = EsModel.cellCtl(this, Role.EDM_TYPE_NAME);
            PersoniumGetResponse gRes = roleDao.get(role);
            if (gRes == null) {
                continue;
            }
            Map<String, Object> src = gRes.getSource();
            Map<String, Object> s = (Map<String, Object>) src.get("s");
            Map<String, Object> l = (Map<String, Object>) src.get("l");
            String roleName = (String) s.get(KEY_NAME);
            String boxId = (String) l.get(Box.EDM_TYPE_NAME);
            String boxName = null;
            String schema = null;
            if (boxId != null) {
                // Box?
                EntitySetAccessor box = EsModel.box(this);
                PersoniumGetResponse getRes = box.get(boxId);
                if (getRes == null || !getRes.isExists()) {
                    continue;
                }
                Map<String, Object> boxsrc = getRes.getSource();
                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);

            ret.add(roleObj);
        }
        return ret;
    }

    @Override
    public List<Role> getRoleListHere(final IExtRoleContainingToken token) {
        List<Role> ret = new ArrayList<Role>();

        // ExtCell?Role????????Role
        this.addRoleListExtCelltoRole(token, ret);

        // ExtCell?Relation?Role????????Role
        // ?
        // ExtCell?Relation?ExtRole?Role????????Role
        this.addRoleListExtCelltoRelationAndExtRole(token, ret);

        return ret;
    }

    /**
     * ExtCell?Role??????????Role?.
     * @param token
     *            
     * @param roles
     *            ???????
     */
    private void addRoleListExtCelltoRole(final IExtRoleContainingToken token, List<Role> roles) {
        // ExtCell-Role??????Role??
        String extCell = token.getExtCellUrl();
        String principal = token.getSubject();
        String principalCell;
        if (principal.contains("#")) {
            principalCell = token.getSubject().substring(0, principal.indexOf("#"));
        } else {
            principalCell = token.getSubject();
        }

        // ?ExtCell???2??????
        if (extCell.equals(principalCell)) {
            ODataProducer op = ModelFactory.ODataCtl.cellCtl(this);
            EntitiesResponse response = null;
            // ?
            QueryInfo qi = QueryInfo.newBuilder().setTop(TOP_NUM).setInlineCount(InlineCount.NONE).build();

            List<String> list = UriUtils.getUrlVariations(this.getUnitUrl(), extCell);
            for (int i = 0; i < list.size(); i++) {
                String extCellUrl = list.get(i);
                try {
                    // ExtCell-Role??
                    response = (EntitiesResponse) op.getNavProperty(ExtCell.EDM_TYPE_NAME,
                            OEntityKey.create(extCellUrl), "_" + Role.EDM_TYPE_NAME, qi);
                } catch (PersoniumCoreException dce) {
                    if (PersoniumCoreException.OData.NO_SUCH_ENTITY != dce) {
                        throw dce;
                    }
                }
                if (response != null) {
                    break;
                }
            }
            if (response == null) {
                return;
            }

            // ExtCell-Role???????????????
            List<OEntity> entList = response.getEntities();
            for (OEntity ent : entList) {
                OEntityWrapper entRole = (OEntityWrapper) ent;
                this.addRole(entRole.getUuid(), roles);
            }
        }
    }

    /**
     * ExtCell?Relation?Role????????Role. ?
     * ExtCell?Relation?ExtRole?Role????????Role.
     * @param token
     *            
     * @param roles
     *            ???????
     */
    @SuppressWarnings("unchecked")
    private void addRoleListExtCelltoRelationAndExtRole(final IExtRoleContainingToken token, List<Role> roles) {
        String extCell = token.getExtCellUrl();

        // ExtCell-Role??????Role??
        ODataProducer op = ModelFactory.ODataCtl.cellCtl(this);
        EntitiesResponse response = null;
        // ?
        QueryInfo qi = QueryInfo.newBuilder().setTop(TOP_NUM).setInlineCount(InlineCount.NONE).build();
        List<String> list = UriUtils.getUrlVariations(this.getUnitUrl(), extCell);
        for (int i = 0; i < list.size(); i++) {
            try {
                String extCellUrl = list.get(i);
                // ExtCell-Relation??
                response = (EntitiesResponse) op.getNavProperty(ExtCell.EDM_TYPE_NAME,
                        OEntityKey.create(extCellUrl), "_" + Relation.EDM_TYPE_NAME, qi);
            } catch (PersoniumCoreException dce) {
                if (PersoniumCoreException.OData.NO_SUCH_ENTITY != dce) {
                    throw dce;
                }
            }
            if (response != null) {
                break;
            }
        }
        if (response == null) {
            return;
        }

        List<OEntity> entList = response.getEntities();
        for (OEntity ent : entList) {
            OEntityWrapper entRelation = (OEntityWrapper) ent;

            // ExtCell-Relation???????????????
            PersoniumSearchResponse res = serchRoleLinks(Relation.EDM_TYPE_NAME, entRelation.getUuid());
            if (res == null) {
                continue;
            }
            this.addRoles(res.getHits().getHits(), roles);
            //  ?????ExtCell?Relation?Role????????Role.?
            //  ????ExtCell?Relation?ExtRole?Role????????Role??.

            // Relation?ExtRole??
            EntitySetAccessor extRoleType = EsModel.cellCtl(this, ExtRole.EDM_TYPE_NAME);

            // Relation??????ExtRole?
            // ????????
            Map<String, Object> source = new HashMap<String, Object>();

            // ?????????
            List<Map<String, Object>> implicitFilters = QueryMapFactory.getImplicitFilters(this.id, null, null,
                    null, extRoleType.getType());
            String linksKey = OEntityDocHandler.KEY_LINK + "." + Relation.EDM_TYPE_NAME;
            implicitFilters.add(0, QueryMapFactory.termQuery(linksKey, entRelation.getUuid()));
            Map<String, Object> query = QueryMapFactory.mustQuery(implicitFilters);
            Map<String, Object> filteredQuery = QueryMapFactory.filteredQuery(null, query);
            source.put("query", filteredQuery);
            long hitNum = extRoleType.count(source);
            // ExtCell???????????
            if (hitNum == 0) {
                continue;
            }
            source.put("size", hitNum);

            PersoniumSearchHits extRoleHits = extRoleType.search(source).getHits();
            // ExtCell???????????
            // ?????????????????
            if (extRoleHits.getCount() == 0) {
                continue;
            }
            for (PersoniumSearchHit extRoleHit : extRoleHits.getHits()) {
                Map<String, Object> extRoleSource = extRoleHit.getSource();
                Map<String, Object> extRoleS = (Map<String, Object>) extRoleSource.get("s");
                String esExtRole = (String) extRoleS.get(ExtRole.EDM_TYPE_NAME);

                // ??????????
                for (Role tokenRole : token.getRoleList()) {
                    if (!tokenRole.createUrl().equals(esExtRole)) {
                        continue;
                    }
                    // ExtCell-Role???????????????
                    PersoniumSearchResponse resExtRoleToRole = serchRoleLinks(ExtRole.EDM_TYPE_NAME,
                            extRoleHit.getId());
                    if (resExtRoleToRole == null) {
                        continue;
                    }
                    this.addRoles(resExtRoleToRole.getHits().getHits(), roles);
                }
            }
        }
    }

    /**
     * Role???????.
     * @param searchKey
     *            ????
     * @param searchValue
     *            ?uuid
     * @return ?
     */
    private PersoniumSearchResponse serchRoleLinks(final String searchKey, final String searchValue) {

        ODataLinkAccessor links = EsModel.cellCtlLink(this);
        // Relation???????
        Map<String, Object> source = new HashMap<String, Object>();
        Map<String, Object> filter = new HashMap<String, Object>();
        Map<String, Object> and = new HashMap<String, Object>();
        List<Map<String, Object>> filters = new ArrayList<Map<String, Object>>();

        List<Map<String, Object>> queries = new ArrayList<Map<String, Object>>();
        queries.add(QueryMapFactory.termQuery("t1", searchKey));
        queries.add(QueryMapFactory.termQuery("t2", "Role"));

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

        filters.add(QueryMapFactory.termQuery("k1", searchValue));
        and.put("filters", filters);
        filter.put("and", and);
        source.put("filter", filter);
        source.put("query", query);
        // ?
        source.put("size", TOP_NUM);

        return links.search(source);
    }

    /**
     * Role????SearchHit??????.
     * @param hits
     *            Role??
     * @param roles
     *            ???????
     */
    private void addRoles(PersoniumSearchHit[] hits, List<Role> roles) {
        for (PersoniumSearchHit hit : hits) {
            Map<String, Object> src = hit.getSource();
            String roleUuid = (String) src.get("k2");

            // Relation-Role???????????????
            this.addRole(roleUuid, roles);
        }
    }

    /**
     * ???.
     * @param uuid
     *            Role?UUID
     * @param roles
     *            ???????
     */
    @SuppressWarnings("unchecked")
    private void addRole(String uuid, List<Role> roles) {
        EntitySetAccessor roleDao = EsModel.cellCtl(this, Role.EDM_TYPE_NAME);
        PersoniumGetResponse gRes = roleDao.get(uuid);
        if (gRes == null) {
            return;
        }
        Map<String, Object> src = gRes.getSource();
        Map<String, Object> s = (Map<String, Object>) src.get("s");
        Map<String, Object> l = (Map<String, Object>) src.get("l");
        String roleName = (String) s.get(KEY_NAME);
        String schema = (String) s.get(KEY_SCHEMA);
        String boxId = (String) l.get(Box.EDM_TYPE_NAME);
        String boxName = null;
        if (boxId != null) {
            // Box?
            Map<String, Object> boxsrc = DavCmpFsImpl.searchBox(this, boxId);
            Map<String, Object> boxs = (Map<String, Object>) boxsrc.get("s");
            boxName = (String) boxs.get(KEY_NAME);
        }

        roles.add(new Role(roleName, boxName, schema, this.url));
    }

    @Override
    public String getOwner() {
        return this.owner;
    }

    @Override
    public String getDataBundleNameWithOutPrefix() {
        String unitUserName;
        if (this.owner == null) {
            unitUserName = AccessContext.TYPE_ANONYMOUS;
        } else {
            unitUserName = IndexNameEncoder.encodeEsIndexName(owner);
        }
        return unitUserName;
    }

    @Override
    public String getDataBundleName() {
        String unitUserName = PersoniumUnitConfig.getEsUnitPrefix() + "_" + getDataBundleNameWithOutPrefix();
        return unitUserName;
    }

    /**
     * Cell??????.
     * @return Cell??
     */
    @Override
    public String getName() {
        return name;
    }

    /**
     * ??Cell?ID???.
     * @return ID
     */
    @Override
    public String getId() {
        return this.id;
    }

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

    /**
     * ??Cell?URL???.
     * @return URL
     */
    @Override
    public String getUrl() {
        return this.url;
    }

    /**
     * ??Cell?URL???.
     * @param url
     *            URL
     */
    public void setUrl(String url) {
        this.url = url;
    }

    /**
     * ??Cell?Unit URL???.
     * @return unitUrl 
     */
    @Override
    public String getUnitUrl() {
        return UriUtils.getUnitUrl(this.getUrl());
    }

    static final String KEY_NAME = "Name";
    static final String KEY_SCHEMA = "Schema";

    /**
     * ??????.
     * @param propValue
     *            
     * @param dcFormat
     *            dcFormat?
     * @return ???false?
     */
    private static boolean validatePropertyRegEx(String propValue, String dcFormat) {
        // ???
        Pattern pattern = Pattern.compile(dcFormat);
        Matcher matcher = pattern.matcher(propValue);
        if (!matcher.matches()) {
            return false;
        }
        return true;
    }

    /**
     * Cell????.
     * @return Cell??
     */
    public long getPublished() {
        return this.published;
    }

    @Override
    public String roleIdToRoleResourceUrl(String roleId) {
        CellCtlODataProducer ccop = new CellCtlODataProducer(this);
        OEntity oe = ccop.getEntityByInternalId(Role.EDM_TYPE_NAME, roleId);
        if (oe == null) {
            // ??????null?
            return null;
        }

        String boxName = (String) oe.getProperty("_Box.Name").getValue();
        OProperty<?> schemaProp = oe.getProperty("_Box.Schema");
        String schema = null;
        if (schemaProp != null) {
            schema = (String) schemaProp.getValue();
        }
        String roleName = (String) oe.getProperty("Name").getValue();
        Role roleObj = new Role(roleName, boxName, schema, this.getUrl());
        return roleObj.createUrl();
    }

    @Override
    public String roleResourceUrlToId(String roleUrl, String baseUrl) {
        EntitySetAccessor roleType = EsModel.cellCtl(this, Role.EDM_TYPE_NAME);

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

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

        // ?URL?ACL?URL??????????
        if (!(this.getUrl().equals(role.getBaseUrl()))) {
            PersoniumCoreLog.Dav.ROLE_NOT_FOUND.params("Cell different").writeLog();
            throw PersoniumCoreException.Dav.ROLE_NOT_FOUND;
        }
        // Role?
        List<Map<String, Object>> queries = new ArrayList<Map<String, Object>>();
        queries.add(QueryMapFactory.termQuery("c", this.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.getBoxForName(role.getBoxName());
            if (targetBox == null) {
                throw PersoniumCoreException.Dav.BOX_LINKED_BY_ROLE_NOT_FOUND.params(baseUrl);
            }
            String boxId = targetBox.getId();
            filters.add(QueryMapFactory.termQuery("l." + Box.EDM_TYPE_NAME, boxId));
        } else {
            // Role????????null
            filters.add(QueryMapFactory.missingFilter("l." + 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);
        PersoniumSearchHits hits = roleType.search(source).getHits();

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

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

    @Override
    public void delete(boolean recursive, String unitUserName) {
        // Cell???????
        int maxLoopCount = Integer.valueOf(PersoniumUnitConfig.getCellLockRetryTimes());
        long interval = Long.valueOf(PersoniumUnitConfig.getCellLockRetryInterval());
        waitCellAccessible(this.id, maxLoopCount, interval);

        CellLockManager.setBulkDeletionStatus(this.id);

        // Cell?
        CellAccessor cellAccessor = (CellAccessor) EsModel.cell();
        CellDocHandler docHandler = new CellDocHandler(cellAccessor.get(this.getId()));
        try {
            cellAccessor.delete(docHandler);
            log.info("Cell Entity Deletion End.");
        } finally {
            CellCache.clear(this.getName());
            CellLockManager.resetBulkDeletionStatus(this.getId());
        }

        // Make this cell empty asynchronously
        Thread thread = new Thread(new Runnable() {
            @Override
            public void run() {
                makeEmpty();
            }
        });
        thread.start();

    }

    private void waitCellAccessible(String cellId, int maxLoopCount, long interval) {
        for (int loopCount = 0; loopCount < maxLoopCount; loopCount++) {
            long count = CellLockManager.getReferenceCount(cellId);
            // ???????????????
            if (count <= 1) {
                return;
            }
            try {
                Thread.sleep(interval);
            } catch (InterruptedException e) {
                throw PersoniumCoreException.Misc.CONFLICT_CELLACCESS;
            }
        }
        throw PersoniumCoreException.Misc.CONFLICT_CELLACCESS;
    }

    private static final int DAVFILE_DEFAULT_FETCH_COUNT = 1000;

    @Override
    public void makeEmpty() {
        CellAccessor cellAccessor = (CellAccessor) EsModel.cell();
        String unitUserNameWithOutPrefix = this.getDataBundleNameWithOutPrefix();
        String cellInfoLog = String.format(" CellId:[%s], CellName:[%s], CellUnitUserName:[%s]", this.getId(),
                this.getName(), this.getDataBundleName());

        // ID????WebDav?????
        long davfileCount = cellAccessor.getDavFileTotalCount(this.getId(), unitUserNameWithOutPrefix);

        // 1000???WebDav?????
        int fetchCount = DAVFILE_DEFAULT_FETCH_COUNT;
        BinaryDataAccessor accessor = new BinaryDataAccessor(PersoniumUnitConfig.getBlobStoreRoot(),
                unitUserNameWithOutPrefix, PersoniumUnitConfig.getPhysicalDeleteMode(),
                PersoniumUnitConfig.getFsyncEnabled());
        for (int i = 0; i <= davfileCount; i += fetchCount) {
            // WebDav?ID??
            List<String> davFileIdList = cellAccessor.getDavFileIdList(this.getId(), unitUserNameWithOutPrefix,
                    fetchCount, i);
            // BinaryDataAccessor?delete??.deleted????
            for (String davFileId : davFileIdList) {
                try {
                    accessor.delete(davFileId);
                } catch (BinaryDataAccessException e) {
                    // ?????????
                    log.warn(String.format("Delete DavFile Failed DavFileId:[%s].", davFileId) + cellInfoLog, e);
                }
            }
        }
        log.info("DavFile Deletion End.");

        // delete EventLog file
        try {
            EventUtils.deleteEventLog(this.getId(), this.getOwner());
            log.info("EventLog Deletion End.");
        } catch (BinaryDataAccessException e) {
            // ?????????
            log.warn("Delete EventLog Failed." + cellInfoLog, e);
        }

        // Cell???
        cellAccessor.cellBulkDeletion(this.getId(), unitUserNameWithOutPrefix);
        log.info("Cell Entity Resource Deletion End.");
    }
}