001/** 002 * Licensed to the Apache Software Foundation (ASF) under one 003 * or more contributor license agreements. See the NOTICE file 004 * distributed with this work for additional information 005 * regarding copyright ownership. The ASF licenses this file 006 * to you under the Apache License, Version 2.0 (the 007 * "License"); you may not use this file except in compliance 008 * with the License. You may obtain a copy of the License at 009 * 010 * http://www.apache.org/licenses/LICENSE-2.0 011 * 012 * Unless required by applicable law or agreed to in writing, software 013 * distributed under the License is distributed on an "AS IS" BASIS, 014 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 015 * See the License for the specific language governing permissions and 016 * limitations under the License. 017 */ 018package org.apache.hadoop.io.erasurecode; 019 020import java.util.Collections; 021import java.util.HashMap; 022import java.util.Map; 023 024import org.apache.hadoop.classification.InterfaceAudience; 025import org.apache.hadoop.classification.InterfaceStability; 026 027/** 028 * Erasure coding schema to housekeeper relevant information. 029 */ 030@InterfaceAudience.Public 031@InterfaceStability.Evolving 032public final class ECSchema { 033 public static final String NUM_DATA_UNITS_KEY = "numDataUnits"; 034 public static final String NUM_PARITY_UNITS_KEY = "numParityUnits"; 035 public static final String CODEC_NAME_KEY = "codec"; 036 037 /** 038 * The erasure codec name associated. 039 */ 040 private final String codecName; 041 042 /** 043 * Number of source data units coded 044 */ 045 private final int numDataUnits; 046 047 /** 048 * Number of parity units generated in a coding 049 */ 050 private final int numParityUnits; 051 052 /* 053 * An erasure code can have its own specific advanced parameters, subject to 054 * itself to interpret these key-value settings. 055 */ 056 private final Map<String, String> extraOptions; 057 058 /** 059 * Constructor with schema name and provided all options. Note the options may 060 * contain additional information for the erasure codec to interpret further. 061 * @param allOptions all schema options 062 */ 063 public ECSchema(Map<String, String> allOptions) { 064 if (allOptions == null || allOptions.isEmpty()) { 065 throw new IllegalArgumentException("No schema options are provided"); 066 } 067 068 this.codecName = allOptions.get(CODEC_NAME_KEY); 069 if (codecName == null || codecName.isEmpty()) { 070 throw new IllegalArgumentException("No codec option is provided"); 071 } 072 073 int tmpNumDataUnits = extractIntOption(NUM_DATA_UNITS_KEY, allOptions); 074 int tmpNumParityUnits = extractIntOption(NUM_PARITY_UNITS_KEY, allOptions); 075 if (tmpNumDataUnits < 0 || tmpNumParityUnits < 0) { 076 throw new IllegalArgumentException( 077 "No good option for numDataUnits or numParityUnits found "); 078 } 079 this.numDataUnits = tmpNumDataUnits; 080 this.numParityUnits = tmpNumParityUnits; 081 082 allOptions.remove(CODEC_NAME_KEY); 083 allOptions.remove(NUM_DATA_UNITS_KEY); 084 allOptions.remove(NUM_PARITY_UNITS_KEY); 085 // After some cleanup 086 this.extraOptions = Collections.unmodifiableMap(allOptions); 087 } 088 089 /** 090 * Constructor with key parameters provided. 091 * @param codecName codec name 092 * @param numDataUnits number of data units used in the schema 093 * @param numParityUnits number os parity units used in the schema 094 */ 095 public ECSchema(String codecName, int numDataUnits, int numParityUnits) { 096 this(codecName, numDataUnits, numParityUnits, null); 097 } 098 099 /** 100 * Constructor with key parameters provided. Note the extraOptions may contain 101 * additional information for the erasure codec to interpret further. 102 * @param codecName codec name 103 * @param numDataUnits number of data units used in the schema 104 * @param numParityUnits number os parity units used in the schema 105 * @param extraOptions extra options to configure the codec 106 */ 107 public ECSchema(String codecName, int numDataUnits, int numParityUnits, 108 Map<String, String> extraOptions) { 109 assert (codecName != null && ! codecName.isEmpty()); 110 assert (numDataUnits > 0 && numParityUnits > 0); 111 112 this.codecName = codecName; 113 this.numDataUnits = numDataUnits; 114 this.numParityUnits = numParityUnits; 115 116 if (extraOptions == null) { 117 extraOptions = new HashMap<>(); 118 } 119 120 // After some cleanup 121 this.extraOptions = Collections.unmodifiableMap(extraOptions); 122 } 123 124 private int extractIntOption(String optionKey, Map<String, String> options) { 125 int result = -1; 126 127 try { 128 if (options.containsKey(optionKey)) { 129 result = Integer.parseInt(options.get(optionKey)); 130 if (result <= 0) { 131 throw new IllegalArgumentException("Bad option value " + result + 132 " found for " + optionKey); 133 } 134 } 135 } catch (NumberFormatException e) { 136 throw new IllegalArgumentException("Option value " + 137 options.get(optionKey) + " for " + optionKey + 138 " is found. It should be an integer"); 139 } 140 141 return result; 142 } 143 144 /** 145 * Get the codec name 146 * @return codec name 147 */ 148 public String getCodecName() { 149 return codecName; 150 } 151 152 /** 153 * Get extra options specific to a erasure code. 154 * @return extra options 155 */ 156 public Map<String, String> getExtraOptions() { 157 return extraOptions; 158 } 159 160 /** 161 * Get required data units count in a coding group 162 * @return count of data units 163 */ 164 public int getNumDataUnits() { 165 return numDataUnits; 166 } 167 168 /** 169 * Get required parity units count in a coding group 170 * @return count of parity units 171 */ 172 public int getNumParityUnits() { 173 return numParityUnits; 174 } 175 176 /** 177 * Make a meaningful string representation for log output. 178 * @return string representation 179 */ 180 @Override 181 public String toString() { 182 StringBuilder sb = new StringBuilder("ECSchema=["); 183 184 sb.append("Codec=" + codecName + ", "); 185 sb.append(NUM_DATA_UNITS_KEY + "=" + numDataUnits + ", "); 186 sb.append(NUM_PARITY_UNITS_KEY + "=" + numParityUnits); 187 sb.append((extraOptions.isEmpty() ? "" : ", ")); 188 189 int i = 0; 190 for (String opt : extraOptions.keySet()) { 191 sb.append(opt + "=" + extraOptions.get(opt) + 192 (++i < extraOptions.size() ? ", " : "")); 193 } 194 195 sb.append("]"); 196 197 return sb.toString(); 198 } 199 200 @Override 201 public boolean equals(Object o) { 202 if (this == o) { 203 return true; 204 } 205 if (o == null || getClass() != o.getClass()) { 206 return false; 207 } 208 209 ECSchema ecSchema = (ECSchema) o; 210 211 if (numDataUnits != ecSchema.numDataUnits) { 212 return false; 213 } 214 if (numParityUnits != ecSchema.numParityUnits) { 215 return false; 216 } 217 if (!codecName.equals(ecSchema.codecName)) { 218 return false; 219 } 220 return extraOptions.equals(ecSchema.extraOptions); 221 } 222 223 @Override 224 public int hashCode() { 225 int result = codecName.hashCode(); 226 result = 31 * result + extraOptions.hashCode(); 227 result = 31 * result + numDataUnits; 228 result = 31 * result + numParityUnits; 229 230 return result; 231 } 232}