package io.divolte.server;
import java.util.Optional;
import org.apache.avro.Schema;
import org.apache.avro.generic.GenericRecord;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import io.divolte.record.DefaultEventRecord;
import io.divolte.server.config.MappingConfiguration;
import io.divolte.server.config.ValidatedConfiguration;
import io.divolte.server.ip2geo.LookupService;
import io.divolte.server.processing.Item;
import io.divolte.server.recordmapping.DslRecordMapper;
import io.divolte.server.recordmapping.DslRecordMapping;
import io.divolte.server.recordmapping.UserAgentParserAndCache;
public class Mapping {
private static final Logger logger = LoggerFactory.getLogger(Mapping.class);
private final DslRecordMapper mapper;
private final boolean keepCorrupted;
private final boolean keepDuplicates;
private final int mappingIndex;
private final IncomingRequestListener listener;
public Mapping(
final ValidatedConfiguration vc,
final String mappingName,
final Optional<LookupService> geoipLookupService,
final SchemaRegistry schemaRegistry,
final IncomingRequestListener listener) {
this.listener = listener;
final MappingConfiguration mappingConfiguration = vc.configuration().mappings.get(mappingName);
final Schema schema = schemaRegistry.getSchemaByMappingName(mappingName);
this.mappingIndex = vc.configuration().mappingIndex(mappingName);
this.keepCorrupted = !mappingConfiguration.discardCorrupted;
this.keepDuplicates = !mappingConfiguration.discardDuplicates;
this.mapper = mappingConfiguration.mappingScriptFile
.map((mappingScriptFile) -> {
logger.info("Using script based schema mapping.");
return new DslRecordMapper(vc, mappingScriptFile, schema, geoipLookupService);
}).orElseGet(() -> {
logger.info("Using built in default schema mapping.");
return new DslRecordMapper(DefaultEventRecord.getClassSchema(), defaultRecordMapping(vc));
});
}
private DslRecordMapping defaultRecordMapping(final ValidatedConfiguration vc) {
final DslRecordMapping result = new DslRecordMapping(DefaultEventRecord.getClassSchema(), new UserAgentParserAndCache(vc), Optional.empty());
result.map("detectedCorruption", result.corrupt());
result.map("detectedDuplicate", result.duplicate());
result.map("firstInSession", result.firstInSession());
result.map("timestamp", result.timestamp());
result.map("clientTimestamp", result.clientTimestamp());
result.map("remoteHost", result.remoteHost());
result.map("referer", result.referer());
result.map("location", result.location());
result.map("viewportPixelWidth", result.viewportPixelWidth());
result.map("viewportPixelHeight", result.viewportPixelHeight());
result.map("screenPixelWidth", result.screenPixelWidth());
result.map("screenPixelHeight", result.screenPixelHeight());
result.map("partyId", result.partyId());
result.map("sessionId", result.sessionId());
result.map("pageViewId", result.pageViewId());
result.map("eventType", result.eventType());
result.map("userAgentString", result.userAgentString());
final DslRecordMapping.UserAgentValueProducer userAgent = result.userAgent();
result.map("userAgentName", userAgent.name());
result.map("userAgentFamily", userAgent.family());
result.map("userAgentVendor", userAgent.vendor());
result.map("userAgentType", userAgent.type());
result.map("userAgentVersion", userAgent.version());
result.map("userAgentDeviceCategory", userAgent.deviceCategory());
result.map("userAgentOsFamily", userAgent.osFamily());
result.map("userAgentOsVersion", userAgent.osVersion());
result.map("userAgentOsVendor", userAgent.osVendor());
return result;
}
public Optional<Item<AvroRecordBuffer>> map(final Item<UndertowEvent> originalIem, final DivolteEvent parsedEvent, final boolean duplicate) {
if (
(keepDuplicates || !duplicate) &&
(keepCorrupted || !parsedEvent.corruptEvent)) {
final GenericRecord avroRecord = mapper.newRecordFromExchange(parsedEvent);
final AvroRecordBuffer avroBuffer = AvroRecordBuffer.fromRecord(
parsedEvent.partyId,
parsedEvent.sessionId,
avroRecord);
/*
* We should really think of a way to get rid of this and test the
* mapping process in isolation of the server.
* In the many-to-many setup, this call is potentially amplified.
*/
listener.incomingRequest(parsedEvent, avroBuffer, avroRecord);
return Optional.of(Item.withCopiedAffinity(mappingIndex, originalIem, avroBuffer));
} else {
return Optional.empty();
}
}
}