11package com .qlangtech .tis .plugins .incr .flink .cdc .pipeline .kafka .sink ;
22
3+ import com .google .common .collect .Maps ;
34import com .qlangtech .plugins .incr .flink .cdc .FlinkCol ;
45import com .qlangtech .tis .async .message .client .consumer .IFlinkColCreator ;
56import com .qlangtech .tis .datax .IDataxProcessor ;
7+ import com .qlangtech .tis .datax .IDataxReader ;
8+ import com .qlangtech .tis .plugin .ds .DBConfig ;
9+ import com .qlangtech .tis .plugin .ds .IDataSourceFactoryGetter ;
610import com .qlangtech .tis .plugin .ds .ISelectedTab ;
711import com .qlangtech .tis .plugins .datax .kafka .writer .DataXKafkaWriter ;
812import com .qlangtech .tis .plugins .incr .flink .cdc .pipeline .PipelineEventSinkFunc ;
913import com .qlangtech .tis .plugins .incr .flink .cdc .pipeline .PipelineFlinkCDCSinkFactory ;
14+ import com .qlangtech .tis .sql .parser .tuple .creator .EntityName ;
15+ import org .apache .flink .api .common .serialization .SerializationSchema ;
16+ import org .apache .flink .cdc .common .configuration .Configuration ;
17+ import org .apache .flink .cdc .common .event .Event ;
1018import org .apache .flink .cdc .common .factories .Factory ;
19+ import org .apache .flink .cdc .common .factories .FactoryHelper ;
20+ import org .apache .flink .cdc .common .pipeline .PipelineOptions ;
1121import org .apache .flink .cdc .common .sink .DataSink ;
22+ import org .apache .flink .cdc .connectors .kafka .json .ChangeLogJsonFormatFactory ;
23+ import org .apache .flink .cdc .connectors .kafka .json .JsonSerializationType ;
24+ import org .apache .flink .cdc .connectors .kafka .sink .KafkaDataSink ;
25+ import org .apache .flink .cdc .connectors .kafka .sink .KafkaDataSinkFactory ;
26+ import org .apache .flink .cdc .connectors .kafka .sink .KafkaDataSinkOptions ;
27+ import org .apache .flink .cdc .connectors .kafka .sink .KeyFormat ;
28+ import org .apache .flink .cdc .connectors .kafka .sink .KeySerializationFactory ;
29+ import org .apache .flink .cdc .connectors .kafka .sink .PartitionStrategy ;
30+ import org .apache .flink .connector .base .DeliveryGuarantee ;
31+ import org .apache .flink .shaded .guava31 .com .google .common .collect .ImmutableMap ;
32+ import org .apache .kafka .clients .producer .ProducerConfig ;
1233
1334import java .time .ZoneId ;
1435import java .util .List ;
36+ import java .util .Map ;
37+ import java .util .Objects ;
1538import java .util .Optional ;
39+ import java .util .Properties ;
40+
41+ import static org .apache .flink .cdc .connectors .kafka .sink .KafkaDataSinkOptions .KEY_FORMAT ;
42+ import static org .apache .flink .cdc .connectors .kafka .sink .KafkaDataSinkOptions .PROPERTIES_PREFIX ;
43+ import static org .apache .flink .cdc .connectors .kafka .sink .KafkaDataSinkOptions .SINK_TABLE_ID_TO_TOPIC_MAPPING ;
1644
1745/**
1846 * <a href="https://nightlies.apache.org/flink/flink-cdc-docs-release-3.5/docs/connectors/pipeline-connectors/kafka/">...</a>
47+ *
1948 * @author 百岁 (baisui@qlangtech.com)
2049 * @date 2026/3/13
2150 * // @see tis-flink-pipeline-paimon-plugin:com.qlangtech.tis.plugins.incr.flink.pipeline.paimon.sink.PaimonPipelineEventSinkFunc
@@ -38,37 +67,149 @@ public KafkaPipelineEventSinkFunc(IDataxProcessor dataxProcessor
3867 , List <ISelectedTab > tabs //
3968 , IFlinkColCreator <FlinkCol > sourceFlinkColCreator //
4069 , int sinkTaskParallelism ) {
41- super (dataxProcessor , pipelineSinkFactory , sinkDBName , tabs , sourceFlinkColCreator , null , sinkTaskParallelism );
70+ super (dataxProcessor , pipelineSinkFactory //
71+ , sinkDBName , tabs , sourceFlinkColCreator , null , sinkTaskParallelism );
72+ }
73+
74+ @ Override
75+ protected EntityName getWriterEntityName (ISelectedTab selTab ) {
76+
77+ IDataxReader reader = this .dataxProcessor .getReader (null );
78+ if (reader instanceof IDataSourceFactoryGetter ) {
79+ try {
80+ String [] databaseName = new String [1 ];
81+ DBConfig dbConfig = ((IDataSourceFactoryGetter ) reader ).getDataSourceFactory ().getDbConfig ();
82+ dbConfig .vistDbName (new DBConfig .IProcess () {
83+ @ Override
84+ public boolean visit (DBConfig config , String jdbcUrl , String ip , String dbName ) throws Exception {
85+ databaseName [0 ] = dbName ;
86+ return true ;
87+ }
88+ });
89+ return EntityName .create (databaseName [0 ], selTab .getName ());
90+ } catch (Exception e ) {
91+ throw new RuntimeException (e );
92+ }
93+ }
94+
95+ return super .getWriterEntityName (selTab );
4296 }
4397
4498 /**
4599 * @param context
46100 * @return
47- * @see org.apache.flink.cdc.connectors.kafka.sink.KafkaDataSink
101+ * @see org.apache.flink.cdc.connectors.kafka.sink.KafkaDataSinkFactory#createDataSink(Factory.Context)
48102 */
103+ // protected DataSink createDataSink(Factory.Context context) {
104+ // KafkaDataSinkFactory factory = new KafkaDataSinkFactory();
105+ // return factory.createDataSink(context);
106+ // }
49107 @ Override
50- protected DataSink createDataSink (Factory .Context context ) {
51- // DeliveryGuarantee deliveryGuarantee,
52- // Properties kafkaProperties,
53- // PartitionStrategy partitionStrategy,
54- // ZoneId zoneId,
55- // SerializationSchema<Event> keySerialization,
56- // SerializationSchema<Event> valueSerialization,
57- // String topic,
58- // boolean addTableToHeaderEnabled,
59- // String customHeaders,
60- // String tableMapping
61-
62- String topic = this .writer .topic ;
63- ZoneId zoneId = this .pipelineSinkFactory .getTimeZone ();
64- // return new KafkaDataSink(catalogOpts, tableOptions, commitUser, partitionMaps, serializer, zoneId,
65- // schemaOperatorUid);
66- return null ;
108+ public DataSink createDataSink (Factory .Context context ) {
109+ KafkaPipelineSinkFactory pipelineSinkFactory = ((KafkaPipelineSinkFactory ) this .pipelineSinkFactory );
110+ KeyFormat keyFormat = context .getFactoryConfiguration ().get (KEY_FORMAT );
111+ JsonSerializationType jsonSerializationType =
112+ context .getFactoryConfiguration ().get (KafkaDataSinkOptions .VALUE_FORMAT );
113+ KafkaDataSinkFactory factory = new KafkaDataSinkFactory ();
114+ FactoryHelper helper = FactoryHelper .createFactoryHelper (factory , context );
115+ helper .validateExcept (
116+ PROPERTIES_PREFIX , keyFormat .toString (), jsonSerializationType .toString ());
117+
118+ DeliveryGuarantee deliveryGuarantee = pipelineSinkFactory .deliveryGuarantee ();
119+ // context.getFactoryConfiguration().get(KafkaDataSinkOptions.DELIVERY_GUARANTEE);
120+ ZoneId zoneId = ZoneId .systemDefault ();
121+ if (!Objects .equals (
122+ context .getPipelineConfiguration ().get (PipelineOptions .PIPELINE_LOCAL_TIME_ZONE ),
123+ PipelineOptions .PIPELINE_LOCAL_TIME_ZONE .defaultValue ())) {
124+ zoneId =
125+ ZoneId .of (
126+ context .getPipelineConfiguration ()
127+ .get (PipelineOptions .PIPELINE_LOCAL_TIME_ZONE ));
128+ }
129+ SerializationSchema <Event > keySerialization =
130+ KeySerializationFactory .createSerializationSchema (
131+ helper .getFormatConfig (keyFormat .toString ()), keyFormat , zoneId );
132+ org .apache .flink .configuration .Configuration formatCfg = pipelineSinkFactory .format .createFlinkCfg ();
133+
134+ SerializationSchema <Event > valueSerialization =
135+ ChangeLogJsonFormatFactory .createSerializationSchema (
136+ formatCfg , // helper.getFormatConfig(jsonSerializationType.toString()),
137+ jsonSerializationType ,
138+ zoneId );
139+ final Properties kafkaProperties = new Properties ();
140+ Map <String , String > allOptions = context .getFactoryConfiguration ().toMap ();
141+ allOptions .keySet ().stream ()
142+ .filter (key -> key .startsWith (PROPERTIES_PREFIX ))
143+ .forEach (
144+ key -> {
145+ final String value = allOptions .get (key );
146+ final String subKey = key .substring ((PROPERTIES_PREFIX ).length ());
147+ kafkaProperties .put (subKey , value );
148+ });
149+ String topic = context .getFactoryConfiguration ().get (KafkaDataSinkOptions .TOPIC );
150+ boolean addTableToHeaderEnabled =
151+ context .getFactoryConfiguration ()
152+ .get (KafkaDataSinkOptions .SINK_ADD_TABLEID_TO_HEADER_ENABLED );
153+ String customHeaders =
154+ context .getFactoryConfiguration ().get (KafkaDataSinkOptions .SINK_CUSTOM_HEADER );
155+ PartitionStrategy partitionStrategy = pipelineSinkFactory .getPartitionStrategy ();
156+ // context.getFactoryConfiguration().get(KafkaDataSinkOptions.PARTITION_STRATEGY);
157+ String tableMapping = context .getFactoryConfiguration ().get (SINK_TABLE_ID_TO_TOPIC_MAPPING );
158+ return new KafkaDataSink (
159+ deliveryGuarantee ,
160+ kafkaProperties ,
161+ partitionStrategy ,
162+ zoneId ,
163+ keySerialization ,
164+ valueSerialization ,
165+ topic ,
166+ addTableToHeaderEnabled ,
167+ customHeaders ,
168+ tableMapping );
67169 }
68170
69171 @ Override
70172 public Factory .Context createDataSinkContext () {
71- return null ;
173+ ImmutableMap .Builder <String , String > factoryParamBuilder = ImmutableMap .<String , String >builder ();
174+
175+ // 1. topic from DataXKafkaWriter
176+ factoryParamBuilder .put (KafkaDataSinkOptions .TOPIC .key (), this .writer .topic );
177+
178+ // 2. value format from KafkaPipelineSinkFactory.format
179+ KafkaPipelineSinkFactory sinkFactory = (KafkaPipelineSinkFactory ) this .pipelineSinkFactory ;
180+ factoryParamBuilder .put (KafkaDataSinkOptions .VALUE_FORMAT .key (),
181+ Objects .requireNonNull (sinkFactory .format , "format can not be null" ).getDescriptor ().getDisplayName ());
182+
183+ // 3. kafka producer properties with "properties." prefix from DataXKafkaWriter
184+ Map <String , Object > kafkaConfig = this .writer .buildKafkaConfig ();
185+ kafkaConfig = Maps .newHashMap (kafkaConfig );
186+ // kafkaConfig.put(ProducerConfig.KEY_SERIALIZER_CLASS_CONFIG,);
187+ // kafkaConfig.put(ProducerConfig.VALUE_SERIALIZER_CLASS_CONFIG,);
188+
189+ kafkaConfig .put (ProducerConfig .KEY_SERIALIZER_CLASS_CONFIG
190+ , "org.apache.flink.kafka.shaded.org.apache.kafka.common.serialization.ByteArraySerializer" );// .StringSerializer.class.getName())
191+ kafkaConfig .put (ProducerConfig .VALUE_SERIALIZER_CLASS_CONFIG
192+ , "org.apache.flink.kafka.shaded.org.apache.kafka.common.serialization.ByteArraySerializer" );//
193+
194+ for (Map .Entry <String , Object > entry : kafkaConfig .entrySet ()) {
195+ // if (ProducerConfig.KEY_SERIALIZER_CLASS_CONFIG.equals(entry.getKey())
196+ // || ProducerConfig.VALUE_SERIALIZER_CLASS_CONFIG.equals(entry.getKey())) {
197+ // continue;
198+ // }
199+ factoryParamBuilder .put (KafkaDataSinkOptions .PROPERTIES_PREFIX + entry .getKey (),
200+ String .valueOf (entry .getValue ()));
201+ }
202+
203+ Configuration factoryConfiguration = Configuration .fromMap (factoryParamBuilder .build ());
204+
205+ // 4. pipeline configuration with timezone
206+ ImmutableMap .Builder <String , String > pipelineParamBuilder = ImmutableMap .<String , String >builder ();
207+ pipelineParamBuilder .put (PipelineOptions .PIPELINE_LOCAL_TIME_ZONE .key (),
208+ this .pipelineSinkFactory .getTimeZone ().getId ());
209+ Configuration pipelineConfiguration = Configuration .fromMap (pipelineParamBuilder .build ());
210+
211+ ClassLoader classLoader = KafkaPipelineEventSinkFunc .class .getClassLoader ();
212+ return new FactoryHelper .DefaultContext (factoryConfiguration , pipelineConfiguration , classLoader );
72213 }
73214
74215
0 commit comments