/* * 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.state; import java.lang.reflect.Type; import java.util.Map; import java.util.Properties; import com.google.common.base.Optional; import com.google.common.collect.Maps; import com.google.gson.Gson; import com.google.gson.reflect.TypeToken; import gobblin.Constructs; import gobblin.configuration.State; import gobblin.configuration.WorkUnitState; /** * Contains the state of a Gobblin construct at the end of a task. It can be merged with the {@link WorkUnitState}, * allowing constructs to mutate the {@link WorkUnitState} (for example for a changed effective watermark), or add * values to report as metadata of task success/failure events. */ public class ConstructState extends State { private static final Gson GSON = new Gson(); private static Type TYPE_OF_HASHMAP = new TypeToken<Map<String, String>>() { }.getType(); private static final String OVERWRITE_PROPS_KEY = "gobblin.util.final.state.overwrite.props"; private static final String FINAL_CONSTRUCT_STATE_PREFIX = "construct.final.state."; public ConstructState() { } public ConstructState(Properties properties) { super(properties); } public ConstructState(State otherState) { super(otherState); } /** * Add a set of properties that will overwrite properties in the {@link WorkUnitState}. * @param properties Properties to override. */ public void addOverwriteProperties(Map<String, String> properties) { Map<String, String> previousOverwriteProps = getOverwritePropertiesMap(); previousOverwriteProps.putAll(properties); setProp(OVERWRITE_PROPS_KEY, serializeMap(previousOverwriteProps)); } /** * Add a set of properties that will overwrite properties in the {@link WorkUnitState}. * @param state Properties to override. */ public void addOverwriteProperties(State state) { Map<String, String> propsMap = Maps.newHashMap(); for (String key : state.getPropertyNames()) { propsMap.put(key, state.getProp(key)); } addOverwriteProperties(propsMap); } /** * See {@link #addConstructState(Constructs, ConstructState, Optional)}. This method uses no infix. */ public void addConstructState(Constructs construct, ConstructState constructState) { addConstructState(construct, constructState, Optional.<String>absent()); } /** * See {@link #addConstructState(Constructs, ConstructState, Optional)}. This is a convenience method to pass a * String infix. */ public void addConstructState(Constructs construct, ConstructState constructState, String infix) { addConstructState(construct, constructState, Optional.of(infix)); } /** * Merge a {@link ConstructState} for a child construct into this {@link ConstructState}. * * <p> * Non-override property names will be mutated as follows: key -> construct.name() + infix + key * </p> * * @param construct type of the child construct. * @param constructState {@link ConstructState} to merge. * @param infix infix added to each non-override key (for example converter number if there are multiple converters). */ public void addConstructState(Constructs construct, ConstructState constructState, Optional<String> infix) { addOverwriteProperties(constructState.getOverwritePropertiesMap()); constructState.removeProp(OVERWRITE_PROPS_KEY); for (String key : constructState.getPropertyNames()) { setProp(construct.name() + "." + (infix.isPresent() ? infix.get() + "." : "") + key, constructState.getProp(key)); } addAll(constructState); } /** * Merge this {@link ConstructState} into a {@link WorkUnitState}. All override properties will be added as-is to the * {@lik WorkUnitState}, and possibly override already present properties. All other properties have their keys * mutated key -> {@link #FINAL_CONSTRUCT_STATE_PREFIX} + key, and added to the {@link WorkUnitState}. */ public void mergeIntoWorkUnitState(WorkUnitState state) { Properties overwriteProperties = getOverwriteProperties(); state.addAll(overwriteProperties); removeProp(OVERWRITE_PROPS_KEY); for (String key : getPropertyNames()) { state.setProp(FINAL_CONSTRUCT_STATE_PREFIX + key, getProp(key)); } } /** * @return a {@link Map} of all override properties. */ public Map<String, String> getOverwritePropertiesMap() { return contains(OVERWRITE_PROPS_KEY) ? deserializeMap(getProp(OVERWRITE_PROPS_KEY)) : Maps.<String, String>newHashMap(); } /** * @return a {@link Properties} object of all override properties. */ public Properties getOverwriteProperties() { Properties props = new Properties(); props.putAll(getOverwritePropertiesMap()); return props; } private static String serializeMap(Map<String, String> map) { return GSON.toJson(map); } private static Map<String, String> deserializeMap(String string) { return GSON.fromJson(string, TYPE_OF_HASHMAP); } }