/*
* Copyright 2016 StreamSets Inc.
*
* Licensed under 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 com.streamsets.pipeline.config.upgrade;
import com.google.common.base.Joiner;
import com.google.common.base.Optional;
import com.streamsets.pipeline.api.Config;
import com.streamsets.pipeline.config.AvroSchemaLookupMode;
import com.streamsets.pipeline.config.DestinationAvroSchemaSource;
import com.streamsets.pipeline.config.OriginAvroSchemaSource;
import java.util.ArrayList;
import java.util.List;
/**
* Utility class for extracting common data format upgrade code
* that can be used in individual stage upgraders until ConfigBean upgrading
* is possible.
*/
public class DataFormatUpgradeHelper {
private static final Joiner PERIOD = Joiner.on(".");
private DataFormatUpgradeHelper() {
}
public static void upgradeAvroParserWithSchemaRegistrySupport(List<Config> configs) {
List<Config> toRemove = new ArrayList<>();
List<Config> toAdd = new ArrayList<>();
Optional<Config> avroSchema = findByName(configs, "avroSchema");
if (!avroSchema.isPresent()) {
throw new IllegalStateException("Config 'avroSchema' is missing, this upgrader cannot be applied.");
}
String configName = avroSchema.get().getName();
String prefix = configName.substring(0, configName.lastIndexOf("."));
// schemaInMessage was removed and superseded by OriginAvroSchemaSource.SOURCE
Optional<Config> schemaInMessage = findByName(configs, "schemaInMessage");
// This upgrader intentionally behaves differently then plain new stage on the canvas. On new stage user is forced
// to chose where is the Avro schema as this decision is no longer "simple". However for people who are upgrading
// we're making the same decision that they selected in the past.
if (schemaInMessage.isPresent()) {
if ((boolean) schemaInMessage.get().getValue()) {
toAdd.add(new Config(PERIOD.join(prefix, "avroSchemaSource"), OriginAvroSchemaSource.SOURCE));
} else {
toAdd.add(new Config(PERIOD.join(prefix, "avroSchemaSource"), OriginAvroSchemaSource.INLINE));
}
toRemove.add(schemaInMessage.get());
} else {
toAdd.add(new Config(PERIOD.join(prefix, "avroSchemaSource"), OriginAvroSchemaSource.SOURCE));
}
// New configs added
toAdd.add(new Config(PERIOD.join(prefix, "schemaRegistryUrls"), new ArrayList<>()));
toAdd.add(new Config(PERIOD.join(prefix, "schemaLookupMode"), AvroSchemaLookupMode.AUTO));
toAdd.add(new Config(PERIOD.join(prefix, "subject"), ""));
toAdd.add(new Config(PERIOD.join(prefix, "schemaId"), 0));
configs.removeAll(toRemove);
configs.addAll(toAdd);
}
public static void upgradeAvroGeneratorWithSchemaRegistrySupport(List<Config> configs) {
List<Config> toRemove = new ArrayList<>();
List<Config> toAdd = new ArrayList<>();
Optional<Config> avroSchema = findByName(configs, "avroSchema");
if (!avroSchema.isPresent()) {
throw new IllegalStateException("Config 'avroSchema' is missing, this upgrader cannot be applied.");
}
String configName = avroSchema.get().getName();
String prefix = configName.substring(0, configName.lastIndexOf("."));
// avroSchemaInHeader was removed and superseded by DestinationAvroSchemaSource.HEADER
Optional<Config> avroSchemaInHeader = findByName(configs, "avroSchemaInHeader");
if (avroSchemaInHeader.isPresent()) {
if ((boolean) avroSchemaInHeader.get().getValue()) {
toAdd.add(new Config(PERIOD.join(prefix, "avroSchemaSource"), DestinationAvroSchemaSource.HEADER));
} else {
toAdd.add(new Config(PERIOD.join(prefix, "avroSchemaSource"), DestinationAvroSchemaSource.INLINE));
}
toRemove.add(avroSchemaInHeader.get());
} else {
toAdd.add(new Config(PERIOD.join(prefix, "avroSchemaSource"), DestinationAvroSchemaSource.INLINE));
}
toAdd.add(new Config(PERIOD.join(prefix, "registerSchema"), false));
toAdd.add(new Config(PERIOD.join(prefix, "schemaRegistryUrlsForRegistration"), new ArrayList<>()));
toAdd.add(new Config(PERIOD.join(prefix, "schemaRegistryUrls"), new ArrayList<>()));
toAdd.add(new Config(PERIOD.join(prefix, "schemaLookupMode"), AvroSchemaLookupMode.AUTO));
toAdd.add(new Config(PERIOD.join(prefix, "subject"), ""));
toAdd.add(new Config(PERIOD.join(prefix, "subjectToRegister"), ""));
toAdd.add(new Config(PERIOD.join(prefix, "schemaId"), 0));
configs.removeAll(toRemove);
configs.addAll(toAdd);
}
/**
* There is a problem with some older stages that originally were not using our data parser library as the
* upgrade on those stages does not create all the properties that were introduced by the data parser library.
* For example File Tail origin doesn't support avro, so the upgrade procedure doesn't use create the avro
* specific properties. This is normally not a problem as post-upgrade SDC will create all missing properties
* with default values. However this particular upgrade will fail if the property avroSchema is missing.
*
* Hence for such stages, this method will ensure that the property avroSchema is properly present. The migration
* to data parser library happened for majority of stages in 1.2 release and hence this really affects only people
* upgrading from 1.1.x all the way to 2.1 or above. If the user upgraded from 1.1.x and then to any other release
* below 2.1 first, they would not hit this issue as the property avroSchema would be added with default value.
*/
public static void ensureAvroSchemaExists(List<Config> configs, String prefix) {
Optional<Config> avroSchema = findByName(configs, "avroSchema");
if (!avroSchema.isPresent()) {
configs.add(new Config(PERIOD.join(prefix, "avroSchema"), null));
}
}
static Optional<Config> findByName(List<Config> configs, String name) {
for (Config config : configs) {
if (config.getName().endsWith(name)) {
return Optional.of(config);
}
}
return Optional.absent();
}
}