/*
* 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 gobblin.converter.initializer;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.Collection;
import java.util.Map;
import javax.sql.DataSource;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.Preconditions;
import com.google.gson.Gson;
import gobblin.configuration.State;
import gobblin.converter.jdbc.AvroToJdbcEntryConverter;
import gobblin.converter.jdbc.JdbcType;
import gobblin.publisher.JdbcPublisher;
import gobblin.source.workunit.WorkUnit;
import gobblin.util.ForkOperatorUtils;
import gobblin.util.jdbc.DataSourceBuilder;
import gobblin.writer.commands.JdbcWriterCommands;
import gobblin.writer.commands.JdbcWriterCommandsFactory;
/**
* Initialize for AvroToJdbcEntryConverter. ConverterInitializer is being invoked at driver which means
* it will only invoked once per converter. This is to remove any duplication work among task, and
* any initialization that is same per task can be put in here.
*/
public class AvroToJdbcEntryConverterInitializer implements ConverterInitializer {
private static final Logger LOG = LoggerFactory.getLogger(AvroToJdbcEntryConverterInitializer.class);
private final State state;
private final Collection<WorkUnit> workUnits;
private final JdbcWriterCommandsFactory jdbcWriterCommandsFactory;
private final int branches;
private final int branchId;
public AvroToJdbcEntryConverterInitializer(State state, Collection<WorkUnit> workUnits) {
this(state, workUnits, new JdbcWriterCommandsFactory(), 1, 0);
}
public AvroToJdbcEntryConverterInitializer(State state, Collection<WorkUnit> workUnits,
JdbcWriterCommandsFactory jdbcWriterCommandsFactory, int branches, int branchId) {
this.state = state;
this.workUnits = workUnits;
this.jdbcWriterCommandsFactory = jdbcWriterCommandsFactory;
this.branches = branches;
this.branchId = branchId;
}
/**
* AvroToJdbcEntryConverter list of date columns existing in the table. As we don't want each converter
* making a connection against database to get the same information. Here, ConverterInitializer will
* retrieve it and store it into WorkUnit so that AvroToJdbcEntryConverter will use it later.
*
* {@inheritDoc}
* @see gobblin.initializer.Initializer#initialize()
*/
@Override
public void initialize() {
String table = Preconditions.checkNotNull(this.state.getProp(ForkOperatorUtils
.getPropertyNameForBranch(JdbcPublisher.JDBC_PUBLISHER_FINAL_TABLE_NAME, this.branches, this.branchId)));
String db = Preconditions.checkNotNull(this.state.getProp(ForkOperatorUtils
.getPropertyNameForBranch(JdbcPublisher.JDBC_PUBLISHER_DATABASE_NAME, this.branches, this.branchId)));
try (Connection conn = createConnection()) {
JdbcWriterCommands commands = this.jdbcWriterCommandsFactory.newInstance(this.state, conn);
Map<String, JdbcType> dateColumnMapping = commands.retrieveDateColumns(db, table);
LOG.info("Date column mapping: " + dateColumnMapping);
final String dateFieldsKey = ForkOperatorUtils.getPropertyNameForBranch(
AvroToJdbcEntryConverter.CONVERTER_AVRO_JDBC_DATE_FIELDS, this.branches, this.branchId);
for (WorkUnit wu : this.workUnits) {
wu.setProp(dateFieldsKey, new Gson().toJson(dateColumnMapping));
}
} catch (SQLException e) {
throw new RuntimeException(e);
}
}
@Override
public void close() {}
@VisibleForTesting
public Connection createConnection() throws SQLException {
DataSource dataSource = DataSourceBuilder.builder().url(this.state.getProp(JdbcPublisher.JDBC_PUBLISHER_URL))
.driver(this.state.getProp(JdbcPublisher.JDBC_PUBLISHER_DRIVER))
.userName(this.state.getProp(JdbcPublisher.JDBC_PUBLISHER_USERNAME))
.passWord(this.state.getProp(JdbcPublisher.JDBC_PUBLISHER_PASSWORD))
.cryptoKeyLocation(this.state.getProp(JdbcPublisher.JDBC_PUBLISHER_ENCRYPTION_KEY_LOC)).maxActiveConnections(1)
.maxIdleConnections(1).state(this.state).build();
return dataSource.getConnection();
}
}