/** * 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.rs.odata; import java.io.Reader; import java.util.ArrayList; import java.util.List; import java.util.Map; import javax.ws.rs.HeaderParam; import javax.ws.rs.core.HttpHeaders; import javax.ws.rs.core.Response; import org.odata4j.core.ODataConstants; import org.odata4j.core.ODataVersion; import org.odata4j.core.OEntity; import org.odata4j.core.OEntityKey; import org.odata4j.core.OProperty; import org.odata4j.edm.EdmComplexType; import org.odata4j.edm.EdmDataServices; import org.odata4j.edm.EdmEntitySet; import org.odata4j.edm.EdmEntityType; import org.odata4j.edm.EdmProperty; import com.fujitsu.dc.core.DcCoreException; import com.fujitsu.dc.core.auth.AccessContext; import com.fujitsu.dc.core.model.ctl.Common; import com.fujitsu.dc.core.odata.OEntityWrapper; /** * ODataのEntityリソースのMERGEメソッドを扱うJAX-RS リソース. */ public class ODataMergeResource extends ODataEntityResource { private final String keyString; private final ODataResource odataResource; private final AccessContext accessContext; private OEntityKey oEntityKey; /** * コンストラクタ. * @param odataResource 親リソースであるODataResource * @param entitySetName EntitySet Name * @param key キー文字列 */ public ODataMergeResource(ODataResource odataResource, String entitySetName, String key) { super(); this.odataResource = odataResource; this.accessContext = this.odataResource.accessContext; setOdataProducer(this.odataResource.getODataProducer()); setEntitySetName(entitySetName); this.keyString = key; try { this.oEntityKey = OEntityKey.parse(this.keyString); } catch (IllegalArgumentException e) { throw DcCoreException.OData.ENTITY_KEY_PARSE_ERROR.reason(e); } } /** * MERGE メソッドの処理. * @param reader リクエストボディ * @param accept Accept ヘッダ * @param ifMatch If-Match ヘッダ * @return JAX-RSResponse */ public Response merge(Reader reader, @HeaderParam(HttpHeaders.ACCEPT) final String accept, @HeaderParam(HttpHeaders.IF_MATCH) final String ifMatch) { // メソッド実行可否チェック checkNotAllowedMethod(); // アクセス制御 this.odataResource.checkAccessContext(this.accessContext, this.odataResource.getNecessaryWritePrivilege(getEntitySetName())); // リクエストからOEntityWrapperを作成する. OEntity oe = this.createRequestEntity(reader, this.oEntityKey); OEntityWrapper oew = new OEntityWrapper(null, oe, null); // 必要ならばメタ情報をつける処理 this.odataResource.beforeMerge(oew, this.oEntityKey); // If-Matchヘッダで入力されたETagをMVCC用での衝突検知用にOEntityWrapperに設定する。 String etag = ODataResource.parseEtagHeader(ifMatch); oew.setEtag(etag); // MERGE処理をODataProducerに依頼。 // こちらでリソースの存在確認もしてもらう。 getOdataProducer().mergeEntity(getEntitySetName(), this.oEntityKey, oew); // 特に例外があがらなければ、レスポンスを返す。 // oewに新たに登録されたETagを返す etag = oew.getEtag(); return Response.noContent() .header(HttpHeaders.ETAG, ODataResource.renderEtagHeader(etag)) .header(ODataConstants.Headers.DATA_SERVICE_VERSION, ODataVersion.V2.asString) .build(); } /** * スキーマ定義をもとにOPropertyにデフォルト値を設定. <br /> * MERGEの場合、キー, updated, published以外の項目にデフォルト値は設定しない * @param ep EdmProperty * @param propName プロパティ名 * @param op OProperty * @param metadata EdmDataServicesスキーマ定義 * @return Oproperty */ @Override protected OProperty<?> setDefaultValue(EdmProperty ep, String propName, OProperty<?> op, EdmDataServices metadata) { if (metadata != null) { // スキーマ情報の取得 EdmEntitySet edmEntitySet = metadata.findEdmEntitySet(getEntitySetName()); EdmEntityType edmEntityType = edmEntitySet.getType(); // スキーマに定義されたキーリストを取得 List<String> keysDefined = edmEntityType.getKeys(); String epName = ep.getName(); // キー, updated, published以外の項目にデフォルト値は設定しない if (!keysDefined.contains(epName) && !Common.P_PUBLISHED.getName().equals(epName) && !Common.P_UPDATED.getName().equals(epName)) { return null; } } return super.setDefaultValue(ep, propName, op, metadata); } /** * ComplexTypeスキーマを参照して、必須チェックとデフォルト値の設定を行う. * @param metadata スキーマ情報 * @param edmComplexType ComplexTypeのスキーマ情報 * @param complexProperties ComplexTypePropertyのList * @return デフォルト値を設定したComplexTypeプロパティの一覧 */ @Override protected List<OProperty<?>> createNewComplexProperties(EdmDataServices metadata, EdmComplexType edmComplexType, Map<String, OProperty<?>> complexProperties) { // ComplexTypeスキーマを参照して、必須チェックとデフォルト値の設定を行う List<OProperty<?>> newComplexProperties = new ArrayList<OProperty<?>>(); for (EdmProperty ctp : edmComplexType.getProperties()) { // プロパティ情報を取得する String compPropName = ctp.getName(); OProperty<?> complexProperty = complexProperties.get(compPropName); if (ctp.getType().isSimple()) { // シンプル型の場合 // MERGEの場合はデフォルト値を設定しない if (complexProperty == null) { continue; } else if (complexProperty.getValue() == null) { // Nullableチェック complexProperty = setDefaultValue(ctp, compPropName, complexProperty); } } else { // Complex型の場合 complexProperty = getComplexProperty(ctp, compPropName, complexProperty, metadata); } if (complexProperty != null) { // MERGEリクエストでは、ComplexTypeのPropertyが指定されていない場合は無視する newComplexProperties.add(complexProperty); } } return newComplexProperties; } }