blob: d670c40e06a2fa658857b7bd53763276580bcae1 [file] [log] [blame]
/*
* 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 org.apache.drill.exec.server.options;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.core.ObjectCodec;
import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.JavaType;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
import com.fasterxml.jackson.databind.deser.std.StdDeserializer;
import org.apache.drill.shaded.guava.com.google.common.base.Preconditions;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.IOException;
/**
* <p>
* This represents a persisted {@link OptionValue}. Decoupling the {@link OptionValue} from what
* is persisted will prevent us from accidentally breaking backward compatibility in the future
* when the {@link OptionValue} changes. Additionally when we do change the format of stored options we
* will not have to change much code since this is already designed with backward compatibility in mind.
* This class is also forward compatible with the Drill Option storage format in Drill 1.11 and earlier.
* </p>
*
* <p>
* <b>Contract:</b>
* Only {@link PersistedOptionValue}s created from an {@link OptionValue} should be persisted.
* And {@link OptionValue}s should only be created from {@link PersistedOptionValue}s that are
* retrieved from a store.
* </p>
*/
// Custom Deserializer required for backward compatibility DRILL-5809
@JsonInclude(JsonInclude.Include.NON_NULL)
@JsonDeserialize(using = PersistedOptionValue.Deserializer.class)
public class PersistedOptionValue {
/**
* This is present for forward compatability with Drill 1.11 and earlier
*/
public static final String SYSTEM_TYPE = "SYSTEM";
/**
* This constant cannot be changed for backward and forward compatibility reasons.
*/
public static final String JSON_TYPE = "type";
/**
* This constant cannot be changed for backward and forward compatibility reasons.
*/
public static final String JSON_KIND = "kind";
/**
* This constant cannot be changed for backward and forward compatibility reasons.
*/
public static final String JSON_NAME = "name";
/**
* This constant cannot be changed for backward and forward compatibility reasons.
*/
public static final String JSON_NUM_VAL = "num_val";
/**
* This constant cannot be changed for backward and forward compatibility reasons.
*/
public static final String JSON_STRING_VAL = "string_val";
/**
* This constant cannot be changed for backward and forward compatibility reasons.
*/
public static final String JSON_BOOL_VAL = "bool_val";
/**
* This constant cannot be changed for backward and forward compatibility reasons.
*/
public static final String JSON_FLOAT_VAL = "float_val";
/**
* This constant cannot be changed for backward and forward compatibility reasons.
*/
public static final String JSON_INTEGER_VAL = "int_val";
private String value;
private OptionValue.Kind kind;
private String name;
private Long num_val;
private String string_val;
private Boolean bool_val;
private Double float_val;
public PersistedOptionValue(String value) {
this.value = Preconditions.checkNotNull(value);
}
public PersistedOptionValue(OptionValue.Kind kind, String name,
Long num_val, String string_val,
Boolean bool_val, Double float_val) {
this.kind = kind;
this.name = name;
this.num_val = num_val;
this.string_val = string_val;
this.bool_val = bool_val;
this.float_val = float_val;
switch (kind) {
case BOOLEAN:
Preconditions.checkNotNull(bool_val);
value = bool_val.toString();
break;
case STRING:
Preconditions.checkNotNull(string_val);
value = string_val;
break;
case DOUBLE:
Preconditions.checkNotNull(float_val);
value = float_val.toString();
break;
case LONG:
Preconditions.checkNotNull(num_val);
value = num_val.toString();
break;
default:
throw new UnsupportedOperationException(String.format("Unsupported type %s", kind));
}
}
/**
* This is ignored for forward compatibility.
*/
@JsonIgnore
public String getValue() {
return value;
}
/**
* This is present for forward compatibility.
*/
@JsonProperty(JSON_TYPE)
public String getType() {
return SYSTEM_TYPE;
}
/**
* This is present for forward compatibility.
*/
@JsonProperty(JSON_KIND)
public OptionValue.Kind getKind() {
return kind;
}
/**
* This is present for forward compatibility.
*/
@JsonProperty(JSON_NAME)
public String getName() {
return name;
}
/**
* This is present for forward compatibility.
*/
@JsonProperty(JSON_NUM_VAL)
public Long getNumVal() {
return num_val;
}
/**
* This is present for forward compatibility.
*/
@JsonProperty(JSON_STRING_VAL)
public String getStringVal() {
return string_val;
}
/**
* This is present for forward compatibility.
*/
@JsonProperty(JSON_BOOL_VAL)
public Boolean getBoolVal() {
return bool_val;
}
/**
* This is present for forward compatibility.
*/
@JsonProperty(JSON_FLOAT_VAL)
public Double getFloatVal() {
return float_val;
}
public OptionValue toOptionValue(final OptionDefinition optionDefinition, final OptionValue.OptionScope optionScope) {
Preconditions.checkNotNull(value, "The value must be defined in order for this to be converted to an " +
"option value");
final OptionValidator validator = optionDefinition.getValidator();
final OptionValue.Kind kind = validator.getKind();
final String name = validator.getOptionName();
final OptionValue.AccessibleScopes accessibleScopes = optionDefinition.getMetaData().getAccessibleScopes();
return OptionValue.create(kind, accessibleScopes, name, value, optionScope);
}
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
PersistedOptionValue that = (PersistedOptionValue) o;
if (value != null ? !value.equals(that.value) : that.value != null) {
return false;
}
if (kind != that.kind) {
return false;
}
if (name != null ? !name.equals(that.name) : that.name != null) {
return false;
}
if (num_val != null ? !num_val.equals(that.num_val) : that.num_val != null) {
return false;
}
if (string_val != null ? !string_val.equals(that.string_val) : that.string_val != null) {
return false;
}
if (bool_val != null ? !bool_val.equals(that.bool_val) : that.bool_val != null) {
return false;
}
return float_val != null ? float_val.equals(that.float_val) : that.float_val == null;
}
@Override
public int hashCode() {
int result = value != null ? value.hashCode() : 0;
result = 31 * result + (kind != null ? kind.hashCode() : 0);
result = 31 * result + (name != null ? name.hashCode() : 0);
result = 31 * result + (num_val != null ? num_val.hashCode() : 0);
result = 31 * result + (string_val != null ? string_val.hashCode() : 0);
result = 31 * result + (bool_val != null ? bool_val.hashCode() : 0);
result = 31 * result + (float_val != null ? float_val.hashCode() : 0);
return result;
}
@Override
public String toString() {
return "PersistedOptionValue{" + "value='" + value + '\'' + ", kind=" + kind + ", name='" + name +
'\'' + ", num_val=" + num_val + ", string_val='" + string_val + '\'' + ", bool_val=" + bool_val +
", float_val=" + float_val + '}';
}
/**
* This deserializer only fetches the relevant information we care about from a store, which is the
* value of an option. This deserializer is essentially future proof since it only requires a value
* to be stored for an option.
*/
@SuppressWarnings("serial")
public static class Deserializer extends StdDeserializer<PersistedOptionValue> {
private static final Logger logger = LoggerFactory.getLogger(Deserializer.class);
private Deserializer() {
super(PersistedOptionValue.class);
}
protected Deserializer(JavaType valueType) {
super(valueType);
}
protected Deserializer(StdDeserializer<?> src) {
super(src);
}
@Override
public PersistedOptionValue deserialize(JsonParser p, DeserializationContext ctxt) throws IOException {
ObjectCodec oc = p.getCodec();
JsonNode node = oc.readTree(p);
String value = null;
if (node.has(OptionValue.JSON_NUM_VAL)) {
value = node.get(OptionValue.JSON_NUM_VAL).asText();
}
if (node.has(OptionValue.JSON_STRING_VAL)) {
value = node.get(OptionValue.JSON_STRING_VAL).asText();
}
if (node.has(OptionValue.JSON_BOOL_VAL)) {
value = node.get(OptionValue.JSON_BOOL_VAL).asText();
}
if (node.has(OptionValue.JSON_FLOAT_VAL)) {
value = node.get(OptionValue.JSON_FLOAT_VAL).asText();
}
if (node.has(OptionValue.JSON_INTEGER_VAL)) {
value = node.get(OptionValue.JSON_INTEGER_VAL).asText();
}
if (value == null) {
logger.error("Invalid json stored {}.", new ObjectMapper().writeValueAsString(node));
}
return new PersistedOptionValue(value);
}
}
}