001/* 002 * (C) Copyright 2018 Nuxeo (http://nuxeo.com/) and others. 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * http://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 * 016 * Contributors: 017 * pierre 018 */ 019package org.nuxeo.ecm.core.io.avro; 020 021import java.util.HashMap; 022import java.util.List; 023import java.util.Map; 024import java.util.Set; 025 026import org.apache.avro.Schema; 027import org.apache.avro.Schema.Field; 028import org.apache.avro.generic.GenericData; 029import org.apache.avro.generic.GenericRecord; 030import org.nuxeo.common.utils.Path; 031import org.nuxeo.ecm.core.api.DocumentModel; 032import org.nuxeo.ecm.core.api.DocumentRef; 033import org.nuxeo.ecm.core.api.IdRef; 034import org.nuxeo.ecm.core.api.impl.DocumentModelImpl; 035import org.nuxeo.ecm.core.api.model.Property; 036import org.nuxeo.runtime.RuntimeServiceException; 037import org.nuxeo.runtime.avro.AvroMapper; 038import org.nuxeo.runtime.avro.AvroService; 039 040/** 041 * @since 10.2 042 */ 043public class DocumentModelMapper extends AvroMapper<DocumentModel, GenericRecord> { 044 045 public DocumentModelMapper(AvroService service) { 046 super(service); 047 } 048 049 @Override 050 public DocumentModel fromAvro(Schema schema, GenericRecord input) { 051 if (!AvroConstants.DOCUMENT_MODEL.equals(getLogicalType(schema))) { 052 throw new RuntimeServiceException("Schema does not match DocumentModel"); 053 } 054 DocumentModel doc = documentModelFromAvro(input); 055 GenericRecord documentTypeRecord = (GenericRecord) input.get(AvroConstants.DOCUMENT_TYPE); 056 for (Field schemaField : documentTypeRecord.getSchema().getFields()) { 057 GenericRecord schemaRecord = (GenericRecord) documentTypeRecord.get(schemaField.name()); 058 List<Field> fields = schemaField.schema().getFields(); 059 Map<String, Object> data = new HashMap<>(fields.size()); 060 for (Field field : fields) { 061 data.put(service.decodeName(field.name()), 062 service.fromAvro(field.schema(), Property.class, schemaRecord.get(field.name()))); 063 } 064 doc.setProperties(service.decodeName(schemaField.name()), data); 065 } 066 return doc; 067 } 068 069 @Override 070 public GenericRecord toAvro(Schema schema, DocumentModel input) { 071 GenericRecord record = new GenericData.Record(schema); 072 if (AvroConstants.DOCUMENT_MODEL.equals(getLogicalType(schema))) { 073 documentModelToAvro(schema, input, record); 074 } else if (AvroConstants.DOCUMENT_TYPE.equals(getLogicalType(schema))) { 075 for (Field field : schema.getFields()) { 076 record.put(field.name(), service.toAvro(field.schema(), input)); 077 } 078 } else { 079 for (Field field : schema.getFields()) { 080 Property p = input.getProperty(service.decodeName(field.name())); 081 record.put(field.name(), service.toAvro(field.schema(), p)); 082 } 083 } 084 return record; 085 } 086 087 @SuppressWarnings("unchecked") 088 protected DocumentModel documentModelFromAvro(GenericRecord input) { 089 String path = (String) input.get(AvroConstants.PATH); 090 String type = (String) input.get(AvroConstants.PRIMARY_TYPE); 091 String uuid = (String) input.get(AvroConstants.UUID); 092 String parentId = (String) input.get(AvroConstants.PARENT_ID); 093 String repositoryName = (String) input.get(AvroConstants.REPOSITORY_NAME); 094 Boolean isProxy = (Boolean) input.get(AvroConstants.IS_PROXY); 095 DocumentRef parentRef = parentId == null ? null : new IdRef(parentId); 096 Set<String> facets = (Set<String>) input.get(AvroConstants.MIXIN_TYPES); 097 DocumentModelImpl doc = new DocumentModelImpl(null, type, uuid, new Path(path), null, parentRef, 098 null, facets, null, repositoryName, isProxy); 099 doc.setIsVersion((Boolean) input.get(AvroConstants.IS_VERSION)); 100 doc.prefetchCurrentLifecycleState((String) input.get(AvroConstants.CURRENT_LIFE_CYCLE_STATE)); 101 return doc; 102 } 103 104 protected void documentModelToAvro(Schema schema, DocumentModel doc, GenericRecord record) { 105 record.put(AvroConstants.UUID, doc.getId()); 106 record.put(AvroConstants.NAME, doc.getName()); 107 record.put(AvroConstants.TITLE, doc.getTitle()); 108 record.put(AvroConstants.PATH, doc.getPathAsString()); 109 record.put(AvroConstants.REPOSITORY_NAME, doc.getRepositoryName()); 110 record.put(AvroConstants.PRIMARY_TYPE, doc.getType()); 111 DocumentRef parentRef = doc.getParentRef(); 112 if (parentRef != null) { 113 record.put(AvroConstants.PARENT_ID, parentRef.toString()); 114 } 115 record.put(AvroConstants.CURRENT_LIFE_CYCLE_STATE, doc.getCurrentLifeCycleState()); 116 if (doc.isVersion()) { 117 record.put(AvroConstants.VERSION_LABEL, doc.getVersionLabel()); 118 record.put(AvroConstants.VERSION_VERSIONABLE_ID, doc.getVersionSeriesId()); 119 } 120 record.put(AvroConstants.IS_PROXY, doc.isProxy()); 121 record.put(AvroConstants.IS_TRASHED, doc.isTrashed()); 122 record.put(AvroConstants.IS_VERSION, doc.isVersion()); 123 record.put(AvroConstants.IS_CHECKEDIN, !doc.isCheckedOut()); 124 record.put(AvroConstants.IS_LATEST_VERSION, doc.isLatestVersion()); 125 record.put(AvroConstants.IS_LATEST_MAJOR_VERSION, doc.isLatestMajorVersion()); 126 record.put(AvroConstants.CHANGE_TOKEN, doc.getChangeToken()); 127 if (doc.getPos() != null) { 128 record.put(AvroConstants.POS, doc.getPos()); 129 } 130 // facets 131 record.put(AvroConstants.MIXIN_TYPES, doc.getFacets()); 132 // document type with schemas 133 record.put(AvroConstants.DOCUMENT_TYPE, service.toAvro(schema.getField(AvroConstants.DOCUMENT_TYPE).schema(), doc)); 134 // INFO \\ tags and acls are ignored for now 135 } 136 137}