All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.apache.kafka.connect.transforms.MaskField Maven / Gradle / Ivy

There is a newer version: 0.10.2.0-bkbase.3
Show newest version
/**
 * 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.kafka.connect.transforms; import org.apache.kafka.common.config.ConfigDef; import org.apache.kafka.connect.connector.ConnectRecord; import org.apache.kafka.connect.data.Field; import org.apache.kafka.connect.data.Schema; import org.apache.kafka.connect.data.Struct; import org.apache.kafka.connect.errors.DataException; import org.apache.kafka.connect.transforms.util.NonEmptyListValidator; import org.apache.kafka.connect.transforms.util.SimpleConfig; import java.math.BigDecimal; import java.math.BigInteger; import java.util.Collections; import java.util.Date; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; import static org.apache.kafka.connect.transforms.util.Requirements.requireMap; import static org.apache.kafka.connect.transforms.util.Requirements.requireStruct; public abstract class MaskField> implements Transformation { public static final String OVERVIEW_DOC = "Mask specified fields with a valid null value for the field type (i.e. 0, false, empty string, and so on)." + "

Use the concrete transformation type designed for the record key (" + Key.class.getCanonicalName() + ") " + "or value (" + Value.class.getCanonicalName() + ")."; private static final String FIELDS_CONFIG = "fields"; public static final ConfigDef CONFIG_DEF = new ConfigDef() .define(FIELDS_CONFIG, ConfigDef.Type.LIST, ConfigDef.NO_DEFAULT_VALUE, new NonEmptyListValidator(), ConfigDef.Importance.HIGH, "Names of fields to mask."); private static final String PURPOSE = "mask fields"; private static final Map, Object> PRIMITIVE_VALUE_MAPPING = new HashMap<>(); static { PRIMITIVE_VALUE_MAPPING.put(Boolean.class, Boolean.FALSE); PRIMITIVE_VALUE_MAPPING.put(Byte.class, (byte) 0); PRIMITIVE_VALUE_MAPPING.put(Short.class, (short) 0); PRIMITIVE_VALUE_MAPPING.put(Integer.class, 0); PRIMITIVE_VALUE_MAPPING.put(Long.class, 0L); PRIMITIVE_VALUE_MAPPING.put(Float.class, 0f); PRIMITIVE_VALUE_MAPPING.put(Double.class, 0d); PRIMITIVE_VALUE_MAPPING.put(BigInteger.class, BigInteger.ZERO); PRIMITIVE_VALUE_MAPPING.put(BigDecimal.class, BigDecimal.ZERO); PRIMITIVE_VALUE_MAPPING.put(Date.class, new Date(0)); PRIMITIVE_VALUE_MAPPING.put(String.class, ""); } private Set maskedFields; @Override public void configure(Map props) { final SimpleConfig config = new SimpleConfig(CONFIG_DEF, props); maskedFields = new HashSet<>(config.getList(FIELDS_CONFIG)); } @Override public R apply(R record) { if (operatingSchema(record) == null) { return applySchemaless(record); } else { return applyWithSchema(record); } } private R applySchemaless(R record) { final Map value = requireMap(operatingValue(record), PURPOSE); final HashMap updatedValue = new HashMap<>(value); for (String field : maskedFields) { updatedValue.put(field, masked(value.get(field))); } return newRecord(record, updatedValue); } private R applyWithSchema(R record) { final Struct value = requireStruct(operatingValue(record), PURPOSE); final Struct updatedValue = new Struct(value.schema()); for (Field field : value.schema().fields()) { final Object origFieldValue = value.get(field); updatedValue.put(field, maskedFields.contains(field.name()) ? masked(origFieldValue) : origFieldValue); } return newRecord(record, updatedValue); } private static Object masked(Object value) { if (value == null) return null; Object maskedValue = PRIMITIVE_VALUE_MAPPING.get(value.getClass()); if (maskedValue == null) { if (value instanceof List) maskedValue = Collections.emptyList(); else if (value instanceof Map) maskedValue = Collections.emptyMap(); else throw new DataException("Cannot mask value of type: " + value.getClass()); } return maskedValue; } @Override public ConfigDef config() { return CONFIG_DEF; } @Override public void close() { } protected abstract Schema operatingSchema(R record); protected abstract Object operatingValue(R record); protected abstract R newRecord(R base, Object value); public static final class Key> extends MaskField { @Override protected Schema operatingSchema(R record) { return record.keySchema(); } @Override protected Object operatingValue(R record) { return record.key(); } @Override protected R newRecord(R record, Object updatedValue) { return record.newRecord(record.topic(), record.kafkaPartition(), record.keySchema(), updatedValue, record.valueSchema(), record.value(), record.timestamp()); } } public static final class Value> extends MaskField { @Override protected Schema operatingSchema(R record) { return record.valueSchema(); } @Override protected Object operatingValue(R record) { return record.value(); } @Override protected R newRecord(R record, Object updatedValue) { return record.newRecord(record.topic(), record.kafkaPartition(), record.keySchema(), record.key(), record.valueSchema(), updatedValue, record.timestamp()); } } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy