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.rs.cell; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.io.Reader; import java.io.UnsupportedEncodingException; import java.net.URI; import java.net.URISyntaxException; 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.HttpHeaders; import javax.ws.rs.core.MediaType; import javax.ws.rs.core.Response; import javax.ws.rs.core.Response.ResponseBuilder; import javax.ws.rs.core.UriInfo; import org.apache.http.HttpResponse; import org.apache.http.HttpStatus; import org.apache.http.client.HttpClient; import org.apache.http.client.methods.HttpPost; import org.apache.http.entity.StringEntity; import org.json.simple.JSONObject; import org.json.simple.parser.JSONParser; import org.json.simple.parser.ParseException; import org.odata4j.core.OCollection; import org.odata4j.core.OCollections; import org.odata4j.core.OComplexObjects; import org.odata4j.core.ODataConstants; import org.odata4j.core.ODataVersion; import org.odata4j.core.OEntity; import org.odata4j.core.OEntityKey; import org.odata4j.core.OObject; import org.odata4j.core.OProperties; import org.odata4j.core.OProperty; import org.odata4j.edm.EdmCollectionType; import org.odata4j.edm.EdmComplexType; import org.odata4j.edm.EdmEntitySet; import org.odata4j.producer.EntitiesResponse; import org.odata4j.producer.EntityResponse; import org.odata4j.producer.InlineCount; import org.odata4j.producer.QueryInfo; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.fujitsu.dc.common.auth.token.Role; import com.fujitsu.dc.common.auth.token.TransCellAccessToken; import com.fujitsu.dc.common.utils.DcCoreUtils; import com.fujitsu.dc.core.DcCoreException; import com.fujitsu.dc.core.auth.OAuth2Helper; import com.fujitsu.dc.core.model.ctl.Common; import com.fujitsu.dc.core.model.ctl.CtlSchema; import com.fujitsu.dc.core.model.ctl.ExtCell; import com.fujitsu.dc.core.model.ctl.ReceivedMessage; import com.fujitsu.dc.core.model.ctl.ReceivedMessagePort; import com.fujitsu.dc.core.model.ctl.Relation; import com.fujitsu.dc.core.model.ctl.SentMessage; import com.fujitsu.dc.core.model.ctl.SentMessagePort; import com.fujitsu.dc.core.model.impl.es.odata.CellCtlODataProducer; import com.fujitsu.dc.core.odata.DcODataProducer; import com.fujitsu.dc.core.odata.OEntityWrapper; import com.fujitsu.dc.core.rs.odata.AbstractODataResource; import com.fujitsu.dc.core.rs.odata.ODataResource; import com.fujitsu.dc.core.utils.HttpClientFactory; import com.fujitsu.dc.core.utils.ODataUtils; import com.fujitsu.dc.core.utils.ResourceUtils; /** * __message?OData?. */ public final class MessageODataResource extends AbstractODataResource { static Logger log = LoggerFactory.getLogger(MessageODataResource.class); private MessageResource odataResource; private Map<String, String> propMap = new HashMap<String, String>(); private String version; /** ??. */ private static final int MAX_SENT_NUM = 1000; /** * . * @param odataResource OData * @param producer OData * @param entityTypeName ?? */ public MessageODataResource(MessageResource odataResource, DcODataProducer producer, String entityTypeName) { this.odataResource = odataResource; setOdataProducer(producer); setEntitySetName(entityTypeName); } /** * PCS Version?. * @param version ? version */ public void setVersion(String version) { this.version = version; } /** * ???Entity??. * @param uriInfo URL * @param reader * @return response */ protected Response createMessage(UriInfo uriInfo, Reader reader) { // responseURL?__ctl? UriInfo resUriInfo = DcCoreUtils.createUriInfo(uriInfo, 2, "__ctl"); // Entity?? Producer?? OEntityWrapper oew = getOEntityWrapper(reader, odataResource, CtlSchema.getEdmDataServicesForMessage().build()); EntityResponse res = getOdataProducer().createEntity(getEntitySetName(), oew); // ??? OEntity ent = res.getEntity(); MediaType outputFormat = MediaType.APPLICATION_JSON_TYPE; List<MediaType> contentTypes = new ArrayList<MediaType>(); contentTypes.add(MediaType.APPLICATION_JSON_TYPE); String key = AbstractODataResource.replaceDummyKeyToNull(ent.getEntityKey().toKeyString()); String responseStr = renderEntityResponse(resUriInfo, res, "json", contentTypes); // ?? responseStr = escapeResponsebody(responseStr); ResponseBuilder rb = getPostResponseBuilder(ent, outputFormat, responseStr, resUriInfo, key); return rb.build(); } /** * ??. * @param reader * @param key Id * @return response */ protected Response changeMessageStatus(Reader reader, String key) { JSONObject body; try { body = ResourceUtils.parseBodyAsJSON(reader); } catch (IOException e1) { throw DcCoreException.OData.JSON_PARSE_ERROR.reason(e1); } catch (ParseException e1) { throw DcCoreException.OData.JSON_PARSE_ERROR.reason(e1); } String status = (String) body.get(ReceivedMessage.MESSAGE_COMMAND); // ?? if (!ReceivedMessage.STATUS_UNREAD.equals(status) && !ReceivedMessage.STATUS_READ.equals(status) && !ReceivedMessage.STATUS_APPROVED.equals(status) && !ReceivedMessage.STATUS_REJECTED.equals(status)) { throw DcCoreException.OData.REQUEST_FIELD_FORMAT_ERROR.params(ReceivedMessage.MESSAGE_COMMAND); } // EdmEntitySet?? EdmEntitySet edmEntitySet = getOdataProducer().getMetadata() .getEdmEntitySet(ReceivedMessagePort.EDM_TYPE_NAME); // OEntityKey?? OEntityKey oEntityKey; try { oEntityKey = OEntityKey.parse("('" + key + "')"); } catch (IllegalArgumentException e) { throw DcCoreException.OData.ENTITY_KEY_PARSE_ERROR.reason(e); } // ???/Producer?? String etag = ((CellCtlODataProducer) getOdataProducer()).changeStatusAndUpdateRelation(edmEntitySet, oEntityKey, status); return Response.noContent().header(HttpHeaders.ETAG, ODataResource.renderEtagHeader(etag)) .header(ODataConstants.Headers.DATA_SERVICE_VERSION, ODataVersion.V2.asString).build(); } /** * ?. * @param props * @param id ?ID */ @Override protected void editProperty(List<OProperty<?>> props, String id) { if (ReceivedMessage.EDM_TYPE_NAME.equals(this.getEntitySetName())) { for (int i = 0; i < props.size(); i++) { if (ReceivedMessagePort.P_SCHEMA.getName().equals(props.get(i).getName())) { // ??Schema????????????? props.remove(i); } else if (ReceivedMessagePort.P_BOX_NAME.getName().equals(props.get(i).getName())) { // ??_Box.Name??????????????? props.set(i, OProperties.string(ReceivedMessagePort.P_BOX_NAME.getName(), (String) null)); } } } else if (SentMessage.EDM_TYPE_NAME.equals(this.getEntitySetName())) { EdmCollectionType collectionType = new EdmCollectionType(SentMessage.P_RESULT.getCollectionKind(), SentMessage.P_RESULT.getType()); // ?API????????Results? OCollection.Builder<OObject> builder = requestReceivedMessage(collectionType, id); // ComplexType????? props.add(OProperties.collection(SentMessage.P_RESULT.getName(), collectionType, builder.build())); for (int i = 0; i < props.size(); i++) { if (SentMessagePort.P_BOX_BOUND.getName().equals(props.get(i).getName())) { // ??BoxBound????????????? props.remove(i); } else if (SentMessagePort.P_BOX_NAME.getName().equals(props.get(i).getName())) { // ??_Box.Name??????????????? props.set(i, OProperties.string(SentMessagePort.P_BOX_NAME.getName(), (String) null)); } else if (SentMessagePort.P_RESULT.getName().equals(props.get(i).getName())) { // ??Result??????????????? props.set(i, OProperties.collection(SentMessage.P_RESULT.getName(), collectionType, builder.build())); } } } } /** * ?API?. * @param collectionType EdmCollectionType * @param idKey ??ID * @return ??? */ private OCollection.Builder<OObject> requestReceivedMessage(final EdmCollectionType collectionType, String idKey) { OCollection.Builder<OObject> builder = OCollections.<OObject>newBuilder(collectionType.getItemType()); // ComplexType??? EdmComplexType ct = SentMessage.COMPLEXTYPE_BUILDER.build(); String fromCellUrl = this.odataResource.getAccessContext().getCell().getUrl(); // ? List<String> toList = createRequestUrl(); // ?? for (String toCellUrl : toList) { List<OProperty<?>> result = null; toCellUrl = formatCellUrl(toCellUrl); // ?API??? TransCellAccessToken token = new TransCellAccessToken(fromCellUrl, fromCellUrl, toCellUrl, new ArrayList<Role>(), ""); // ('ID')?ID??? Pattern formatPattern = Pattern.compile("\\('(.+)'\\)"); Matcher formatMatcher = formatPattern.matcher(idKey); formatMatcher.matches(); String id = formatMatcher.group(1); // ?API??? JSONObject requestBody = createRequestJsonBody(fromCellUrl, toCellUrl, toList, id); // ?API? result = requestHttpReceivedMessage(token, toCellUrl, requestBody); // ????? builder.add(OComplexObjects.create(ct, result)); } return builder; } /** * ?. * @return ??CellURL? */ private List<String> createRequestUrl() { List<String> toList = new ArrayList<String>(); String requestToStr = null; String requestToRelationStr = null; requestToStr = this.propMap.get(SentMessage.P_TO.getName()); requestToRelationStr = this.propMap.get(SentMessage.P_TO_RELATION.getName()); // ?To?CellURL? if (requestToStr != null) { String[] uriList = requestToStr.split(","); for (String uri : uriList) { toList.add(uri); } } // ?ToRelation?CellURL?? if (requestToRelationStr != null) { List<String> extCellUrlList = getExtCellListFromToRelation(requestToRelationStr); toList.addAll(extCellUrlList); } List<String> formatToList = new ArrayList<String>(); // ? for (String to : toList) { // ?"/" if (!to.endsWith("/")) { to += "/"; } // To?ToRelation????????? if (!formatToList.contains(to)) { formatToList.add(to); } } checkMaxDestinationsSize(formatToList.size()); return formatToList; } /** * ToRelation???Relation???ExtCell?URL??. * @param toRelation ToRelation? * @return extCellUrlList ExtCell?URL */ protected List<String> getExtCellListFromToRelation(String toRelation) { List<String> extCellUrlList = new ArrayList<String>(); String keyString = AbstractODataResource.replaceNullToDummyKeyWithParenthesis("'" + toRelation + "'"); OEntityKey oEntityKey = OEntityKey.parse(keyString); EntitiesResponse response = null; try { QueryInfo query = new QueryInfo(InlineCount.ALLPAGES, MAX_SENT_NUM, null, null, null, null, null, null, null); response = (EntitiesResponse) getOdataProducer().getNavProperty(Relation.EDM_TYPE_NAME, oEntityKey, "_" + ExtCell.EDM_TYPE_NAME, query); } catch (DcCoreException e) { if (DcCoreException.OData.NO_SUCH_ENTITY.getCode().equals(e.getCode())) { // ToRelation???Relation??????400? throw DcCoreException.SentMessage.TO_RELATION_NOT_FOUND_ERROR.params(toRelation); } else { throw e; } } // ToRelation????ExtCell?URL? List<OEntity> extCellEntities = response.getEntities(); checkMaxDestinationsSize(response.getInlineCount()); for (OEntity extCell : extCellEntities) { extCellUrlList.add(extCell.getProperty(ExtCell.P_URL.getName()).getValue().toString()); } if (extCellUrlList.isEmpty()) { // ToRelation???Relation????ExtCell??????400? throw DcCoreException.SentMessage.RELATED_EXTCELL_NOT_FOUND_ERROR.params(toRelation); } return extCellUrlList; } /** * ?URL????????. * @param destinationsSize ?URL? */ public void checkMaxDestinationsSize(int destinationsSize) { if (destinationsSize > MAX_SENT_NUM) { throw DcCoreException.SentMessage.OVER_MAX_SENT_NUM; } } /** * ?API???. * @param fromCellUrl ?CellURL * @param targetCellUrl ?CellURL * @param toList * @param id ??ID * @return */ @SuppressWarnings("unchecked") private JSONObject createRequestJsonBody(String fromCellUrl, String targetCellUrl, List<String> toList, String id) { JSONObject requestBody = new JSONObject(); String type = this.propMap.get(SentMessage.P_TYPE.getName()); // Status? String status = null; if (ReceivedMessage.TYPE_MESSAGE.equals(type)) { status = ReceivedMessage.STATUS_UNREAD; } else { status = ReceivedMessage.STATUS_NONE; } // MulticastTo? String multicastTo = null; StringBuilder sbMulticastTo = null; for (String to : toList) { String formatTo = formatCellUrl(to); if (!formatTo.equals(targetCellUrl)) { if (sbMulticastTo == null) { sbMulticastTo = new StringBuilder(); } else { sbMulticastTo.append(","); } sbMulticastTo.append(formatTo); } } if (sbMulticastTo != null) { multicastTo = sbMulticastTo.toString(); } requestBody.put(ReceivedMessage.P_ID.getName(), id); requestBody.put(ReceivedMessage.P_IN_REPLY_TO.getName(), this.propMap.get(SentMessage.P_IN_REPLY_TO.getName())); requestBody.put(ReceivedMessage.P_FROM.getName(), fromCellUrl); requestBody.put(ReceivedMessage.P_MULTICAST_TO.getName(), multicastTo); requestBody.put(ReceivedMessage.P_TYPE.getName(), type); requestBody.put(ReceivedMessage.P_TITLE.getName(), this.propMap.get(SentMessage.P_TITLE.getName())); requestBody.put(ReceivedMessage.P_BODY.getName(), this.propMap.get(SentMessage.P_BODY.getName())); requestBody.put(ReceivedMessage.P_PRIORITY.getName(), Integer.valueOf(this.propMap.get(SentMessage.P_PRIORITY.getName()))); requestBody.put(ReceivedMessage.P_STATUS.getName(), status); requestBody.put(ReceivedMessage.P_REQUEST_RELATION.getName(), this.propMap.get(SentMessage.P_REQUEST_RELATION.getName())); requestBody.put(ReceivedMessage.P_REQUEST_RELATION_TARGET.getName(), this.propMap.get(SentMessage.P_REQUEST_RELATION_TARGET.getName())); return requestBody; } /** * CellURL???. * @param cellUrl cell?Url * @return ? */ private String formatCellUrl(String cellUrl) { String formatCellUrl = cellUrl; if (!cellUrl.endsWith("/")) { formatCellUrl += "/"; } return formatCellUrl; } /** * ?API?. * @param token * @param requestCellUrl CellURL * @param jsonBody * @return ? */ private List<OProperty<?>> requestHttpReceivedMessage(TransCellAccessToken token, String requestCellUrl, JSONObject jsonBody) { String requestUrl = requestCellUrl + "__message/port"; // ??? HttpClient client = HttpClientFactory.create(HttpClientFactory.TYPE_INSECURE); HttpPost req = new HttpPost(requestUrl); // StringEntity body = null; try { body = new StringEntity(jsonBody.toJSONString(), "UTF-8"); } catch (UnsupportedEncodingException e) { throw DcCoreException.SentMessage.SM_BODY_PARSE_ERROR.reason(e); } req.setEntity(body); req.addHeader(DcCoreUtils.HttpHeaders.X_DC_VERSION, this.version); req.addHeader(HttpHeaders.AUTHORIZATION, OAuth2Helper.Scheme.BEARER_CREDENTIALS_PREFIX + token.toTokenString()); req.addHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON); req.addHeader(HttpHeaders.ACCEPT, MediaType.APPLICATION_JSON); // ? HttpResponse objResponse = null; try { objResponse = client.execute(req); } catch (Exception ioe) { throw DcCoreException.SentMessage.SM_CONNECTION_ERROR.reason(ioe); } // ??? String statusCode = Integer.toString(objResponse.getStatusLine().getStatusCode()); List<OProperty<?>> properties = new ArrayList<OProperty<?>>(); properties.add(OProperties.string(SentMessage.P_RESULT_TO.getName(), requestCellUrl)); properties.add(OProperties.string(SentMessage.P_RESULT_CODE.getName(), statusCode)); if (Integer.toString(HttpStatus.SC_CREATED).equals(statusCode)) { properties.add(OProperties.string(SentMessage.P_RESULT_REASON.getName(), "Created.")); } else { properties.add(OProperties.string(SentMessage.P_RESULT_REASON.getName(), getErrorMessage(objResponse))); } return properties; } /** * HttpResponse??. * @param objResponse HttpResponse * @return */ private String getErrorMessage(HttpResponse objResponse) { JSONObject resBody = bodyAsJson(objResponse); String message = ""; try { if (resBody != null) { message = (String) ((JSONObject) resBody.get("message")).get("value"); } } catch (Exception e) { log.info(e.getMessage()); } return message; } /** * HttpResponse?JSON??. * @param objResponse HttpResponse * @return */ private JSONObject bodyAsJson(HttpResponse objResponse) { JSONObject body = null; InputStream is = null; InputStreamReader isr = null; BufferedReader reader = null; String bodyString = null; try { objResponse.getHeaders("Content-Encoding"); is = objResponse.getEntity().getContent(); isr = new InputStreamReader(is, "UTF-8"); reader = new BufferedReader(isr); StringBuffer sb = new StringBuffer(); int chr; while ((chr = is.read()) != -1) { sb.append((char) chr); } bodyString = sb.toString(); } catch (IllegalStateException e) { log.info(e.getMessage()); } catch (IOException e) { log.info(e.getMessage()); } finally { try { if (reader != null) { reader.close(); } if (isr != null) { isr.close(); } if (is != null) { is.close(); } } catch (IOException e) { log.info(e.getMessage()); } } try { if (bodyString != null) { body = (JSONObject) new JSONParser().parse(bodyString); } } catch (ParseException e) { log.info(e.getMessage()); } return body; } /** * ??. * @param props OProperty */ @Override public void validate(List<OProperty<?>> props) { // ??????? if (ReceivedMessage.EDM_TYPE_NAME.equals(this.getEntitySetName())) { MessageODataResource.validateUriCsv(ReceivedMessage.P_MULTICAST_TO.getName(), propMap.get(ReceivedMessage.P_MULTICAST_TO.getName())); MessageODataResource.validateBody(propMap.get(ReceivedMessage.P_BODY.getName()), Common.MAX_MESSAGE_BODY_LENGTH); MessageODataResource.validateStatus(propMap.get(ReceivedMessage.P_TYPE.getName()), propMap.get(ReceivedMessage.P_STATUS.getName())); MessageODataResource.validateReqRelation(propMap.get(ReceivedMessage.P_TYPE.getName()), propMap.get(ReceivedMessage.P_REQUEST_RELATION.getName()), propMap.get(ReceivedMessage.P_REQUEST_RELATION_TARGET.getName())); } // ??????? if (SentMessage.EDM_TYPE_NAME.equals(this.getEntitySetName())) { MessageODataResource.validateUriCsv(SentMessage.P_TO.getName(), propMap.get(SentMessage.P_TO.getName())); MessageODataResource.validateBody(propMap.get(SentMessage.P_BODY.getName()), Common.MAX_MESSAGE_BODY_LENGTH); MessageODataResource.validateToAndToRelation(propMap.get(SentMessage.P_TO.getName()), propMap.get(SentMessage.P_TO_RELATION.getName())); MessageODataResource.validateToValue(propMap.get(SentMessage.P_TO.getName()), this.odataResource.getAccessContext().getBaseUri()); MessageODataResource.validateReqRelation(propMap.get(SentMessage.P_TYPE.getName()), propMap.get(SentMessage.P_REQUEST_RELATION.getName()), propMap.get(SentMessage.P_REQUEST_RELATION_TARGET.getName())); } } /** * ??. * @param props * @param */ @Override public void collectProperties(List<OProperty<?>> props) { for (OProperty<?> property : props) { if (property.getValue() == null) { propMap.put(property.getName(), null); } else { propMap.put(property.getName(), property.getValue().toString()); } } } /** * MulticastTo??. * @param propKey * @param propValue */ public static void validateUriCsv(String propKey, String propValue) { if (propValue == null) { return; } if (propValue.contains(",")) { // CSV??????? String[] uriList = propValue.split(","); for (String uri : uriList) { if (uri.length() != 0) { if (!ODataUtils.isValidUri(propKey, uri)) { throw DcCoreException.OData.REQUEST_FIELD_FORMAT_ERROR.params(propKey); } } else { throw DcCoreException.OData.REQUEST_FIELD_FORMAT_ERROR.params(propKey); } } } else { // CSV??????? if (!ODataUtils.isValidUri(propKey, propValue)) { throw DcCoreException.OData.REQUEST_FIELD_FORMAT_ERROR.params(propValue); } } } /** * To????baseUrl??????. * @param toValue to??? * @param baseUrl baseUrl */ public static void validateToValue(String toValue, String baseUrl) { if (toValue == null) { return; } // URL?? String checkBaseUrl = null; String[] uriList = toValue.split(","); for (String uriStr : uriList) { try { URI uri = new URI(uriStr); checkBaseUrl = uri.getScheme() + "://" + uri.getHost(); int port = uri.getPort(); if (port != -1) { checkBaseUrl += ":" + Integer.toString(port); } checkBaseUrl += "/"; } catch (URISyntaxException e) { log.info(e.getMessage()); throw DcCoreException.OData.REQUEST_FIELD_FORMAT_ERROR.params(SentMessage.P_TO.getName()); } if (checkBaseUrl == null || !checkBaseUrl.equals(baseUrl)) { throw DcCoreException.OData.REQUEST_FIELD_FORMAT_ERROR.params(SentMessage.P_TO.getName()); } } } /** * Body??. * @param value ? * @param maxLength */ public static void validateBody(String value, int maxLength) { if (value.getBytes().length > maxLength) { throw DcCoreException.OData.REQUEST_FIELD_FORMAT_ERROR.params(ReceivedMessage.P_BODY.getName()); } } /** * Status??. * @param type * @param status */ public static void validateStatus(String type, String status) { // Type?message??unread // Type?req.relation.build/req.relation.break?? none if (!(ReceivedMessage.TYPE_MESSAGE.equals(type) && ReceivedMessage.STATUS_UNREAD.equals(status)) && !((ReceivedMessage.TYPE_REQ_RELATION_BUILD.equals(type) || ReceivedMessage.TYPE_REQ_RELATION_BREAK.equals(type)) && ReceivedMessage.STATUS_NONE.equals(status))) { throw DcCoreException.OData.REQUEST_FIELD_FORMAT_ERROR.params(ReceivedMessage.P_STATUS.getName()); } } /** * RequestRelation??. * @param type * @param requestRelation ?URL * @param requestRelationTarget ?CellURL */ public static void validateReqRelation(String type, String requestRelation, String requestRelationTarget) { if ((ReceivedMessage.TYPE_REQ_RELATION_BUILD.equals(type) || ReceivedMessage.TYPE_REQ_RELATION_BREAK.equals(type)) && (requestRelation == null || requestRelationTarget == null)) { String detail = ReceivedMessage.P_REQUEST_RELATION.getName() + "," + ReceivedMessage.P_REQUEST_RELATION_TARGET.getName(); throw DcCoreException.OData.REQUEST_FIELD_FORMAT_ERROR.params(detail); } } /** * To-ToRelation??. * @param to ?URL * @param toRelation ???? */ public static void validateToAndToRelation(String to, String toRelation) { if (to == null && toRelation == null) { String detail = SentMessage.P_TO.getName() + "," + SentMessage.P_TO_RELATION.getName(); throw DcCoreException.OData.REQUEST_FIELD_FORMAT_ERROR.params(detail); } } }