Java tutorial
/** * 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(); } }