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 io.personium.core.rs.odata; import java.io.Reader; import java.io.StringWriter; import java.util.regex.Matcher; import java.util.regex.Pattern; import javax.ws.rs.DELETE; import javax.ws.rs.GET; import javax.ws.rs.HttpMethod; import javax.ws.rs.OPTIONS; import javax.ws.rs.POST; import javax.ws.rs.PUT; import javax.ws.rs.QueryParam; import javax.ws.rs.core.Context; import javax.ws.rs.core.MultivaluedMap; import javax.ws.rs.core.Response; import javax.ws.rs.core.UriInfo; import org.apache.http.HttpStatus; import org.core4j.Enumerable; import org.odata4j.core.ODataConstants; import org.odata4j.core.ODataVersion; import org.odata4j.core.OEntityId; import org.odata4j.core.OEntityIds; import org.odata4j.core.OEntityKey; import org.odata4j.edm.EdmDataServices; import org.odata4j.edm.EdmMultiplicity; import org.odata4j.format.FormatParser; import org.odata4j.format.FormatParserFactory; import org.odata4j.format.FormatType; import org.odata4j.format.FormatWriter; import org.odata4j.format.Settings; import org.odata4j.format.SingleLink; import org.odata4j.format.SingleLinks; import org.odata4j.producer.EntityIdResponse; import org.odata4j.producer.ODataProducer; import org.odata4j.producer.QueryInfo; import org.odata4j.producer.exceptions.NotFoundException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import io.personium.common.utils.PersoniumCoreUtils; import io.personium.core.PersoniumCoreException; import io.personium.core.auth.AccessContext; import io.personium.core.model.ctl.Account; import io.personium.core.model.ctl.Common; import io.personium.core.model.ctl.ReceivedMessage; import io.personium.core.model.impl.es.odata.EsODataProducer; import io.personium.core.odata.PersoniumFormatWriterFactory; /** * OData?$links? JAX-RS Resource. */ public final class ODataLinksResource { private final OEntityId sourceEntity; private final String targetNavProp; private final OEntityKey targetEntityKey; private final ODataResource odataResource; private final ODataProducer odataProducer; private final AccessContext accessContext; /** * . */ static Logger log = LoggerFactory.getLogger(ODataLinksResource.class); /** * . * @param odataResource ? ODataResource * @param sourceEntity Entity * @param targetNavProp Navigation Property * @param targetEntityKey EntityKey */ public ODataLinksResource(final ODataResource odataResource, final OEntityId sourceEntity, final String targetNavProp, final OEntityKey targetEntityKey) { this.odataResource = odataResource; this.accessContext = this.odataResource.getAccessContext(); this.odataProducer = this.odataResource.getODataProducer(); this.sourceEntity = sourceEntity; this.targetNavProp = targetNavProp; this.targetEntityKey = targetEntityKey; } /** * POST??? link??. * ????204.?????Location???? * InsertLink Request * If an InsertLink Request is successful, the response MUST have a 204 status code, * as specified in [RFC2616], and contain an empty response body. * @param uriInfo UriInfo * @param reqBody * @return JAX-RS Response */ @POST public Response createLink(@Context UriInfo uriInfo, final Reader reqBody) { // this.checkWriteAccessContext(); // ??? this.odataResource.beforeLinkCreate(this.sourceEntity, this.targetNavProp); // $links ? POST?Nav Prop?????????? if (this.targetEntityKey != null) { throw PersoniumCoreException.OData.KEY_FOR_NAVPROP_SHOULD_NOT_BE_SPECIFIED; } log.debug("POSTING $LINK"); OEntityId newTargetEntity = parseRequestUri( PersoniumCoreUtils.createUriInfo(uriInfo, NUM_LEVELS_FROM_SVC_ROOT), reqBody); // URL????Body????????? Pattern p = Pattern.compile("(.+)/([^/]+)$"); Matcher m = p.matcher(newTargetEntity.getEntitySetName()); String bodyNavProp = m.replaceAll("$2"); String targetEntitySetName = null; // ???$links????Account if (ReceivedMessage.EDM_NPNAME_FOR_ACCOUNT.equals(this.targetNavProp)) { targetEntitySetName = Account.EDM_TYPE_NAME; // ???$links????ReceivedMessage } else if (Account.EDM_NPNAME_FOR_RECEIVED_MESSAGE.equals(this.targetNavProp)) { targetEntitySetName = ReceivedMessage.EDM_TYPE_NAME; } else { targetEntitySetName = this.targetNavProp.substring(1); } if (!targetEntitySetName.equals(bodyNavProp)) { throw PersoniumCoreException.OData.REQUEST_FIELD_FORMAT_ERROR.params(Common.P_FORMAT_PATTERN_URI); } this.odataProducer.createLink(sourceEntity, targetNavProp, newTargetEntity); return noContent(); } /** * PUT??? link?. * @param uriInfo UriInfo * @param reqBody * @return JAX-RS Response */ @PUT public Response updateLink(@Context UriInfo uriInfo, final Reader reqBody) { // this.checkWriteAccessContext(); if (this.targetEntityKey == null) { throw PersoniumCoreException.OData.KEY_FOR_NAVPROP_SHOULD_BE_SPECIFIED; } else { throw PersoniumCoreException.Misc.METHOD_NOT_IMPLEMENTED; } } /** * ???????????$links?OEntityId??. * @param uriInfo URL * @param reqBody * @param srcEntitySetName $linksEntitySet?? * @param metadata * @return $links?OEntityId */ static OEntityId parseRequestUri(final UriInfo uriInfo, final Reader reqBody, String srcEntitySetName, EdmDataServices metadata) { Settings settings = new Settings(ODataVersion.V1, metadata, srcEntitySetName, null, null); FormatParser<SingleLink> parser = FormatParserFactory.getParser(SingleLink.class, FormatType.JSON, settings); SingleLink link = null; try { link = parser.parse(reqBody); } catch (Exception e) { throw PersoniumCoreException.OData.JSON_PARSE_ERROR.reason(e); } if (link.getUri() == null) { throw PersoniumCoreException.OData.REQUEST_FIELD_FORMAT_ERROR.params("uri"); } log.debug(uriInfo.getBaseUri().toASCIIString()); // ? // null??????????null??????????? String linkUrl = AbstractODataResource.replaceNullToDummyKey(link.getUri()); log.debug(linkUrl); OEntityId oid = null; String serviceRootUri = uriInfo.getBaseUri().toASCIIString(); try { oid = OEntityIds.parse(serviceRootUri, linkUrl); } catch (IllegalArgumentException e) { throw PersoniumCoreException.OData.REQUEST_FIELD_FORMAT_ERROR.params("uri"); } // parse?????????????????? String entityId = linkUrl; if (entityId.toLowerCase().startsWith(serviceRootUri.toLowerCase())) { entityId = linkUrl.substring(serviceRootUri.length()); } int indexOfParen = entityId.indexOf('('); String entitySetName = entityId.substring(indexOfParen); Pattern p = Pattern.compile("^\\(.+\\)$"); Matcher m = p.matcher(entitySetName); if (!m.find()) { throw PersoniumCoreException.OData.REQUEST_FIELD_FORMAT_ERROR.params("uri"); } return oid; } /** * ???????????$links?OEntityId??. * @param uriInfo URL * @param reqBody * @return $links?OEntityId */ private OEntityId parseRequestUri(final UriInfo uriInfo, final Reader reqBody) { return parseRequestUri(uriInfo, reqBody, this.sourceEntity.getEntitySetName(), this.odataProducer.getMetadata()); } private Response noContent() { return Response.noContent().header(ODataConstants.Headers.DATA_SERVICE_VERSION, ODataVersion.V2.asString) .build(); } /** * DELETE??? link?. * @return JAX-RS Response */ @DELETE public Response deleteLink() { // this.checkWriteAccessContext(); // ?? this.odataResource.beforeLinkDelete(this.sourceEntity, this.targetNavProp); // TODO $links ? ??2????????NavProp?Key? // 1. http://host/service.svc/Customers('ALFKI')/$links/Orders(1) // 2. http://host/service.svc/Orders(1)/$links/Customer. if (this.targetEntityKey == null) { throw PersoniumCoreException.OData.KEY_FOR_NAVPROP_SHOULD_BE_SPECIFIED; } this.odataProducer.deleteLink(sourceEntity, targetNavProp, targetEntityKey); return noContent(); } static final int NUM_LEVELS_FROM_SVC_ROOT = 3; /** * GET??? link?. * @param uriInfo UriInfo * @param format $format * @param callback ?? * @return JAX-RS Response */ @GET public Response getLinks(@Context final UriInfo uriInfo, @QueryParam("$format") final String format, @QueryParam("$callback") final String callback) { // this.checkReadAccessContext(); if (this.targetEntityKey != null) { return Response.status(HttpStatus.SC_BAD_REQUEST) .entity("targetId should not be specified in $links GET. your value = " + this.targetEntityKey.toKeyString()) .build(); } log.debug("GETTING $LINK"); // ??? this.odataResource.beforeLinkGet(this.sourceEntity, this.targetNavProp); EntityIdResponse response = getLinks(uriInfo); StringWriter sw = new StringWriter(); // context.getRequest().getAcceptableMediaTypes() UriInfo uriInfo2 = PersoniumCoreUtils.createUriInfo(uriInfo, NUM_LEVELS_FROM_SVC_ROOT); String serviceRootUri = uriInfo2.getBaseUri().toASCIIString(); String contentType; if (response.getMultiplicity() == EdmMultiplicity.MANY) { SingleLinks links = SingleLinks.create(serviceRootUri, response.getEntities()); // TODO ??JSON??. FormatWriter<SingleLinks> fw = PersoniumFormatWriterFactory.getFormatWriter(SingleLinks.class, null, "json", callback); fw.write(uriInfo2, sw, links); contentType = fw.getContentType(); } else { OEntityId entityId = Enumerable.create(response.getEntities()).firstOrNull(); if (entityId == null) { throw new NotFoundException(); } SingleLink link = SingleLinks.create(serviceRootUri, entityId); FormatWriter<SingleLink> fw = PersoniumFormatWriterFactory.getFormatWriter(SingleLink.class, null, "json", callback); fw.write(uriInfo, sw, link); contentType = fw.getContentType(); } String entity = sw.toString(); return Response.ok(entity, contentType) .header(ODataConstants.Headers.DATA_SERVICE_VERSION, ODataVersion.V2.asString).build(); } /** * OPTIONS. * @return JAX-RS Response */ @OPTIONS public Response options() { // this.odataResource.checkAccessContext(this.accessContext, this.odataResource.getNecessaryOptionsPrivilege()); return PersoniumCoreUtils .responseBuilderForOptions(HttpMethod.GET, HttpMethod.DELETE, HttpMethod.PUT, HttpMethod.POST) .build(); } private EntityIdResponse getLinks(UriInfo uriInfo) { QueryInfo queryInfo = null; if (uriInfo != null) { queryInfo = queryInfo(uriInfo); } EntityIdResponse response = null; // getLinks?????????????? // ? if (this.odataProducer instanceof EsODataProducer) { EsODataProducer producer = (EsODataProducer) this.odataProducer; response = producer.getLinks(sourceEntity, targetNavProp, queryInfo); } return response; } QueryInfo queryInfo(UriInfo uriInfo) { MultivaluedMap<String, String> mm = uriInfo.getQueryParameters(true); Integer top = QueryParser.parseTopQuery(mm.getFirst("$top")); Integer skip = QueryParser.parseSkipQuery(mm.getFirst("$skip")); return new QueryInfo(null, top, skip, null, null, null, null, null, null); } private void checkWriteAccessContext() { // // TODO BOX???????2???checkAccessContext?Privilege?????? String entitySetNameFrom = sourceEntity.getEntitySetName(); String entitySetNameTo = targetNavProp; if (entitySetNameFrom.equals(ReceivedMessage.EDM_TYPE_NAME) || entitySetNameTo.equals(Account.EDM_NPNAME_FOR_RECEIVED_MESSAGE)) { this.odataResource.checkAccessContext(this.accessContext, this.odataResource.getNecessaryWritePrivilege(ReceivedMessage.EDM_TYPE_NAME)); } else { this.odataResource.checkAccessContext(this.accessContext, this.odataResource.getNecessaryWritePrivilege(entitySetNameFrom)); this.odataResource.checkAccessContext(this.accessContext, this.odataResource.getNecessaryWritePrivilege(entitySetNameTo.substring(1))); } } private void checkReadAccessContext() { // // TODO BOX???????2???checkAccessContext?Privilege?????? String entitySetNameFrom = sourceEntity.getEntitySetName(); String entitySetNameTo = targetNavProp; if (entitySetNameFrom.equals(ReceivedMessage.EDM_TYPE_NAME) || entitySetNameTo.equals(Account.EDM_NPNAME_FOR_RECEIVED_MESSAGE)) { this.odataResource.checkAccessContext(this.accessContext, this.odataResource.getNecessaryReadPrivilege(ReceivedMessage.EDM_TYPE_NAME)); } else { this.odataResource.checkAccessContext(this.accessContext, this.odataResource.getNecessaryReadPrivilege(entitySetNameFrom)); this.odataResource.checkAccessContext(this.accessContext, this.odataResource.getNecessaryReadPrivilege(entitySetNameTo.substring(1))); } } }