/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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 org.apache.nifi.provenance.schema;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.apache.nifi.provenance.ProvenanceEventRecord;
import org.apache.nifi.provenance.ProvenanceEventType;
import org.apache.nifi.provenance.StandardProvenanceEventRecord;
import org.apache.nifi.repository.schema.FieldMapRecord;
import org.apache.nifi.repository.schema.Record;
import org.apache.nifi.repository.schema.RecordField;
import org.apache.nifi.repository.schema.RecordSchema;
public class EventRecord implements Record {
private final RecordSchema schema;
private final ProvenanceEventRecord event;
private final long eventId;
private final Record contentClaimRecord;
private final Record previousClaimRecord;
public EventRecord(final ProvenanceEventRecord event, final long eventId, final RecordSchema schema, final RecordSchema contentClaimSchema) {
this.schema = schema;
this.event = event;
this.eventId = eventId;
this.contentClaimRecord = createContentClaimRecord(contentClaimSchema, event.getContentClaimContainer(), event.getContentClaimSection(),
event.getContentClaimIdentifier(), event.getContentClaimOffset(), event.getFileSize());
this.previousClaimRecord = createContentClaimRecord(contentClaimSchema, event.getPreviousContentClaimContainer(), event.getPreviousContentClaimSection(),
event.getPreviousContentClaimIdentifier(), event.getPreviousContentClaimOffset(), event.getPreviousFileSize());
}
@Override
public RecordSchema getSchema() {
return schema;
}
private static Record createContentClaimRecord(final RecordSchema contentClaimSchema, final String container, final String section,
final String identifier, final Long offset, final Long size) {
if (container == null || section == null || identifier == null) {
return null;
}
final Map<RecordField, Object> fieldValues = new HashMap<>();
fieldValues.put(EventRecordFields.CONTENT_CLAIM_CONTAINER, container);
fieldValues.put(EventRecordFields.CONTENT_CLAIM_SECTION, section);
fieldValues.put(EventRecordFields.CONTENT_CLAIM_IDENTIFIER, identifier);
fieldValues.put(EventRecordFields.CONTENT_CLAIM_OFFSET, offset);
fieldValues.put(EventRecordFields.CONTENT_CLAIM_SIZE, size);
return new FieldMapRecord(fieldValues, contentClaimSchema);
}
@Override
public Object getFieldValue(final String fieldName) {
switch (fieldName) {
case EventFieldNames.EVENT_IDENTIFIER:
return eventId;
case EventFieldNames.ALTERNATE_IDENTIFIER:
return event.getAlternateIdentifierUri();
case EventFieldNames.CHILD_UUIDS:
return event.getChildUuids();
case EventFieldNames.COMPONENT_ID:
return event.getComponentId();
case EventFieldNames.COMPONENT_TYPE:
return event.getComponentType();
case EventFieldNames.CONTENT_CLAIM:
return contentClaimRecord;
case EventFieldNames.EVENT_DETAILS:
return event.getDetails();
case EventFieldNames.EVENT_DURATION:
return event.getEventDuration();
case EventFieldNames.EVENT_TIME:
return event.getEventTime();
case EventFieldNames.EVENT_TYPE:
return event.getEventType().name();
case EventFieldNames.FLOWFILE_ENTRY_DATE:
return event.getFlowFileEntryDate();
case EventFieldNames.FLOWFILE_UUID:
return event.getFlowFileUuid();
case EventFieldNames.LINEAGE_START_DATE:
return event.getLineageStartDate();
case EventFieldNames.PARENT_UUIDS:
return event.getParentUuids();
case EventFieldNames.PREVIOUS_ATTRIBUTES:
return event.getPreviousAttributes();
case EventFieldNames.PREVIOUS_CONTENT_CLAIM:
return previousClaimRecord;
case EventFieldNames.RELATIONSHIP:
return event.getRelationship();
case EventFieldNames.SOURCE_QUEUE_IDENTIFIER:
return event.getSourceQueueIdentifier();
case EventFieldNames.SOURCE_SYSTEM_FLOWFILE_IDENTIFIER:
return event.getSourceSystemFlowFileIdentifier();
case EventFieldNames.TRANSIT_URI:
return event.getTransitUri();
case EventFieldNames.UPDATED_ATTRIBUTES:
return event.getUpdatedAttributes();
}
return null;
}
@SuppressWarnings("unchecked")
public static StandardProvenanceEventRecord getEvent(final Record record, final String storageFilename, final long storageByteOffset, final int maxAttributeLength) {
final StandardProvenanceEventRecord.Builder builder = new StandardProvenanceEventRecord.Builder();
builder.setAlternateIdentifierUri((String) record.getFieldValue(EventFieldNames.ALTERNATE_IDENTIFIER));
builder.setChildUuids((List<String>) record.getFieldValue(EventFieldNames.CHILD_UUIDS));
builder.setComponentId((String) record.getFieldValue(EventFieldNames.COMPONENT_ID));
builder.setComponentType((String) record.getFieldValue(EventFieldNames.COMPONENT_TYPE));
builder.setDetails((String) record.getFieldValue(EventFieldNames.EVENT_DETAILS));
builder.setEventDuration((Long) record.getFieldValue(EventFieldNames.EVENT_DURATION));
builder.setEventTime((Long) record.getFieldValue(EventFieldNames.EVENT_TIME));
builder.setEventType(ProvenanceEventType.valueOf((String) record.getFieldValue(EventFieldNames.EVENT_TYPE)));
builder.setFlowFileEntryDate((Long) record.getFieldValue(EventFieldNames.FLOWFILE_ENTRY_DATE));
builder.setFlowFileUUID((String) record.getFieldValue(EventFieldNames.FLOWFILE_UUID));
builder.setLineageStartDate((Long) record.getFieldValue(EventFieldNames.LINEAGE_START_DATE));
builder.setParentUuids((List<String>) record.getFieldValue(EventFieldNames.PARENT_UUIDS));
builder.setPreviousAttributes(truncateAttributes((Map<String, String>) record.getFieldValue(EventFieldNames.PREVIOUS_ATTRIBUTES), maxAttributeLength));
builder.setRelationship((String) record.getFieldValue(EventFieldNames.RELATIONSHIP));
builder.setSourceQueueIdentifier((String) record.getFieldValue(EventFieldNames.SOURCE_QUEUE_IDENTIFIER));
builder.setSourceSystemFlowFileIdentifier((String) record.getFieldValue(EventFieldNames.SOURCE_SYSTEM_FLOWFILE_IDENTIFIER));
builder.setTransitUri((String) record.getFieldValue(EventFieldNames.TRANSIT_URI));
builder.setUpdatedAttributes(truncateAttributes((Map<String, String>) record.getFieldValue(EventFieldNames.UPDATED_ATTRIBUTES), maxAttributeLength));
final Long eventId = (Long) record.getFieldValue(EventFieldNames.EVENT_IDENTIFIER);
if (eventId != null) {
builder.setEventId(eventId);
}
builder.setStorageLocation(storageFilename, storageByteOffset);
final Record currentClaimRecord = (Record) record.getFieldValue(EventFieldNames.CONTENT_CLAIM);
if (currentClaimRecord == null) {
builder.setCurrentContentClaim(null, null, null, null, 0L);
} else {
builder.setCurrentContentClaim(
(String) currentClaimRecord.getFieldValue(EventFieldNames.CONTENT_CLAIM_CONTAINER),
(String) currentClaimRecord.getFieldValue(EventFieldNames.CONTENT_CLAIM_SECTION),
(String) currentClaimRecord.getFieldValue(EventFieldNames.CONTENT_CLAIM_IDENTIFIER),
(Long) currentClaimRecord.getFieldValue(EventFieldNames.CONTENT_CLAIM_OFFSET),
(Long) currentClaimRecord.getFieldValue(EventFieldNames.CONTENT_CLAIM_SIZE));
}
final Record previousClaimRecord = (Record) record.getFieldValue(EventFieldNames.PREVIOUS_CONTENT_CLAIM);
if (previousClaimRecord != null) {
builder.setPreviousContentClaim(
(String) previousClaimRecord.getFieldValue(EventFieldNames.CONTENT_CLAIM_CONTAINER),
(String) previousClaimRecord.getFieldValue(EventFieldNames.CONTENT_CLAIM_SECTION),
(String) previousClaimRecord.getFieldValue(EventFieldNames.CONTENT_CLAIM_IDENTIFIER),
(Long) previousClaimRecord.getFieldValue(EventFieldNames.CONTENT_CLAIM_OFFSET),
(Long) previousClaimRecord.getFieldValue(EventFieldNames.CONTENT_CLAIM_SIZE));
}
return builder.build();
}
private static Map<String, String> truncateAttributes(final Map<String, String> attributes, final int maxAttributeLength) {
if (attributes == null) {
return null;
}
// Check if any attribute value exceeds the attribute length
final boolean anyExceedsLength = attributes.values().stream()
.filter(value -> value != null)
.anyMatch(value -> value.length() > maxAttributeLength);
if (!anyExceedsLength) {
return attributes;
}
final Map<String, String> truncated = new HashMap<>();
for (final Map.Entry<String, String> entry : attributes.entrySet()) {
final String key = entry.getKey();
final String value = entry.getValue();
if (value == null || value.length() <= maxAttributeLength) {
truncated.put(key, value);
continue;
}
truncated.put(key, value.substring(0, maxAttributeLength));
}
return truncated;
}
}