2020import com .google .auto .service .AutoService ;
2121import com .google .auto .value .AutoValue ;
2222import java .util .Arrays ;
23- import java .util .Collection ;
2423import java .util .Collections ;
2524import java .util .List ;
26- import java .util .stream .Collectors ;
2725import org .apache .beam .sdk .coders .RowCoder ;
26+ import org .apache .beam .sdk .schemas .AutoValueSchema ;
2827import org .apache .beam .sdk .schemas .Schema ;
28+ import org .apache .beam .sdk .schemas .annotations .DefaultSchema ;
29+ import org .apache .beam .sdk .schemas .annotations .SchemaFieldDescription ;
2930import org .apache .beam .sdk .schemas .transforms .SchemaTransform ;
3031import org .apache .beam .sdk .schemas .transforms .SchemaTransformProvider ;
3132import org .apache .beam .sdk .schemas .transforms .TypedSchemaTransformProvider ;
@@ -73,6 +74,19 @@ protected DebeziumReadSchemaTransformProvider(
7374 this .testLimitMilliseconds = timeLimitMs ;
7475 }
7576
77+ private static Connectors parseConnector (String value ) {
78+ try {
79+ return Connectors .valueOf (value );
80+ } catch (IllegalArgumentException e ) {
81+ throw new IllegalArgumentException (
82+ "Unsupported connector '"
83+ + value
84+ + "'. Supported connectors are: "
85+ + Arrays .toString (Connectors .values ()),
86+ e );
87+ }
88+ }
89+
7690 @ Override
7791 protected @ NonNull @ Initialized Class <DebeziumReadSchemaTransformConfiguration >
7892 configurationClass () {
@@ -82,21 +96,15 @@ protected DebeziumReadSchemaTransformProvider(
8296 @ Override
8397 protected @ NonNull @ Initialized SchemaTransform from (
8498 DebeziumReadSchemaTransformConfiguration configuration ) {
85- // TODO(pabloem): Validate configuration parameters to ensure formatting is correct.
99+
100+ configuration .validate ();
101+ Connectors connector = parseConnector (configuration .getDatabase ());
102+
86103 return new SchemaTransform () {
87104 @ Override
88105 public PCollectionRowTuple expand (PCollectionRowTuple input ) {
89106 // TODO(pabloem): Test this behavior
90- Collection <String > connectors =
91- Arrays .stream (Connectors .values ()).map (Object ::toString ).collect (Collectors .toSet ());
92- if (!connectors .contains (configuration .getDatabase ())) {
93- throw new IllegalArgumentException (
94- "Unsupported database "
95- + configuration .getDatabase ()
96- + ". Unable to select a JDBC driver for it. Supported Databases are: "
97- + String .join (", " , connectors ));
98- }
99- Class <?> connectorClass = Connectors .valueOf (configuration .getDatabase ()).getConnector ();
107+ Class <?> connectorClass = connector .getConnector ();
100108 DebeziumIO .ConnectorConfiguration connectorConfiguration =
101109 DebeziumIO .ConnectorConfiguration .create ()
102110 .withUsername (configuration .getUsername ())
@@ -123,9 +131,9 @@ public PCollectionRowTuple expand(PCollectionRowTuple input) {
123131 configuration .getDebeziumConnectionProperties ();
124132 if (debeziumConnectionProperties != null ) {
125133 for (String connectionProperty : debeziumConnectionProperties ) {
126- String [] parts = connectionProperty .split ( "=" , - 1 );
127- String key = parts [ 0 ] ;
128- String value = parts [ 1 ] ;
134+ int separator = connectionProperty .indexOf ( '=' );
135+ String key = connectionProperty . substring ( 0 , separator ) ;
136+ String value = connectionProperty . substring ( separator + 1 ) ;
129137 connectorConfiguration = connectorConfiguration .withConnectionProperty (key , value );
130138 }
131139 }
@@ -138,6 +146,15 @@ public PCollectionRowTuple expand(PCollectionRowTuple input) {
138146 readTransform
139147 .withMaxNumberOfRecords (testLimitRecords )
140148 .withMaxTimeToRun (testLimitMilliseconds );
149+ } else {
150+ Integer maxNumberOfRecords = configuration .getMaxNumberOfRecords ();
151+ if (maxNumberOfRecords != null ) {
152+ readTransform = readTransform .withMaxNumberOfRecords (maxNumberOfRecords );
153+ }
154+ Long maxTimeToRun = configuration .getMaxTimeToRun ();
155+ if (maxTimeToRun != null ) {
156+ readTransform = readTransform .withMaxTimeToRun (maxTimeToRun );
157+ }
141158 }
142159
143160 // TODO(pabloem): Database connection issues can be debugged here.
@@ -172,29 +189,76 @@ public PCollectionRowTuple expand(PCollectionRowTuple input) {
172189 return Collections .singletonList ("output" );
173190 }
174191
192+ @ DefaultSchema (AutoValueSchema .class )
175193 @ AutoValue
176194 public abstract static class DebeziumReadSchemaTransformConfiguration {
195+
196+ @ SchemaFieldDescription ("Maximum number of records to read before stopping." )
197+ public abstract @ Nullable Integer getMaxNumberOfRecords ();
198+
199+ @ SchemaFieldDescription ("Maximum time in milliseconds to run before stopping." )
200+ public abstract @ Nullable Long getMaxTimeToRun ();
201+
202+ @ SchemaFieldDescription ("Username used to connect to the source database." )
177203 public abstract String getUsername ();
178204
205+ @ SchemaFieldDescription ("Password used to connect to the source database." )
179206 public abstract String getPassword ();
180207
208+ @ SchemaFieldDescription ("Hostname of the source database." )
181209 public abstract String getHost ();
182210
211+ @ SchemaFieldDescription ("Port of the source database." )
183212 public abstract int getPort ();
184213
214+ @ SchemaFieldDescription ("Fully qualified table name included in the Debezium change stream." )
185215 public abstract String getTable ();
186216
217+ @ SchemaFieldDescription (
218+ "Debezium connector type. Supported values: MYSQL, POSTGRES, SQLSERVER, ORACLE, and DB2." )
187219 public abstract @ NonNull String getDatabase ();
188220
221+ @ SchemaFieldDescription ("Additional Debezium connection properties in key=value format." )
189222 public abstract @ Nullable List <String > getDebeziumConnectionProperties ();
190223
224+ public void validate () {
225+ if (getHost ().isEmpty ()) {
226+ throw new IllegalArgumentException ("host must not be empty." );
227+ }
228+
229+ if (getPort () <= 0 || getPort () > 65535 ) {
230+ throw new IllegalArgumentException (
231+ "port must be between 1 and 65535, but was " + getPort () + "." );
232+ }
233+
234+ if (getTable ().isEmpty ()) {
235+ throw new IllegalArgumentException ("table must not be empty." );
236+ }
237+
238+ List <String > connectionProperties = getDebeziumConnectionProperties ();
239+ if (connectionProperties != null ) {
240+ for (String property : connectionProperties ) {
241+ if (property == null || property .indexOf ('=' ) <= 0 ) {
242+ throw new IllegalArgumentException (
243+ "Invalid Debezium connection property '"
244+ + property
245+ + "'. Expected key=value format." );
246+ }
247+ }
248+ }
249+ }
250+
191251 public static Builder builder () {
192252 return new AutoValue_DebeziumReadSchemaTransformProvider_DebeziumReadSchemaTransformConfiguration
193253 .Builder ();
194254 }
195255
196256 @ AutoValue .Builder
197257 public abstract static class Builder {
258+ public abstract Builder setMaxNumberOfRecords (@ Nullable Integer maxNumberOfRecords );
259+
260+ public abstract Builder setMaxTimeToRun (@ Nullable Long maxTimeToRun );
261+
198262 public abstract Builder setUsername (String username );
199263
200264 public abstract Builder setPassword (String password );
0 commit comments