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 */ 018 019package org.apache.hadoop.registry.client.types; 020 021import com.google.common.base.Preconditions; 022import org.apache.hadoop.classification.InterfaceAudience; 023import org.apache.hadoop.classification.InterfaceStability; 024import org.apache.hadoop.registry.client.exceptions.InvalidRecordException; 025import org.codehaus.jackson.annotate.JsonAnyGetter; 026import org.codehaus.jackson.annotate.JsonAnySetter; 027import org.codehaus.jackson.map.annotate.JsonSerialize; 028 029import java.util.ArrayList; 030import java.util.HashMap; 031import java.util.List; 032import java.util.Map; 033 034/** 035 * JSON-marshallable description of a single component. 036 * It supports the deserialization of unknown attributes, but does 037 * not support their creation. 038 */ 039@InterfaceAudience.Public 040@InterfaceStability.Evolving 041@JsonSerialize(include = JsonSerialize.Inclusion.NON_NULL) 042public class ServiceRecord implements Cloneable { 043 044 /** 045 * A type string which MUST be in the serialized json. This permits 046 * fast discarding of invalid entries 047 */ 048 public static final String RECORD_TYPE = "JSONServiceRecord"; 049 050 /** 051 * The type field. This must be the string {@link #RECORD_TYPE} 052 */ 053 public String type = RECORD_TYPE; 054 055 /** 056 * Description string 057 */ 058 public String description; 059 060 /** 061 * map to handle unknown attributes. 062 */ 063 private Map<String, String> attributes = new HashMap<String, String>(4); 064 065 /** 066 * List of endpoints intended for use to external callers 067 */ 068 public List<Endpoint> external = new ArrayList<Endpoint>(); 069 070 /** 071 * List of endpoints for use <i>within</i> an application. 072 */ 073 public List<Endpoint> internal = new ArrayList<Endpoint>(); 074 075 /** 076 * Create a service record with no ID, description or registration time. 077 * Endpoint lists are set to empty lists. 078 */ 079 public ServiceRecord() { 080 } 081 082 /** 083 * Deep cloning constructor 084 * @param that service record source 085 */ 086 public ServiceRecord(ServiceRecord that) { 087 this.description = that.description; 088 // others 089 Map<String, String> thatAttrs = that.attributes; 090 for (Map.Entry<String, String> entry : thatAttrs.entrySet()) { 091 attributes.put(entry.getKey(), entry.getValue()); 092 } 093 // endpoints 094 List<Endpoint> src = that.internal; 095 if (src != null) { 096 internal = new ArrayList<Endpoint>(src.size()); 097 for (Endpoint endpoint : src) { 098 internal.add(new Endpoint(endpoint)); 099 } 100 } 101 src = that.external; 102 if (src != null) { 103 external = new ArrayList<Endpoint>(src.size()); 104 for (Endpoint endpoint : src) { 105 external.add(new Endpoint(endpoint)); 106 } 107 } 108 } 109 110 /** 111 * Add an external endpoint 112 * @param endpoint endpoint to set 113 */ 114 public void addExternalEndpoint(Endpoint endpoint) { 115 Preconditions.checkArgument(endpoint != null); 116 endpoint.validate(); 117 external.add(endpoint); 118 } 119 120 /** 121 * Add an internal endpoint 122 * @param endpoint endpoint to set 123 */ 124 public void addInternalEndpoint(Endpoint endpoint) { 125 Preconditions.checkArgument(endpoint != null); 126 endpoint.validate(); 127 internal.add(endpoint); 128 } 129 130 /** 131 * Look up an internal endpoint 132 * @param api API 133 * @return the endpoint or null if there was no match 134 */ 135 public Endpoint getInternalEndpoint(String api) { 136 return findByAPI(internal, api); 137 } 138 139 /** 140 * Look up an external endpoint 141 * @param api API 142 * @return the endpoint or null if there was no match 143 */ 144 public Endpoint getExternalEndpoint(String api) { 145 return findByAPI(external, api); 146 } 147 148 /** 149 * Handle unknown attributes by storing them in the 150 * {@link #attributes} map 151 * @param key attribute name 152 * @param value attribute value. 153 */ 154 @JsonAnySetter 155 public void set(String key, Object value) { 156 attributes.put(key, value.toString()); 157 } 158 159 /** 160 * The map of "other" attributes set when parsing. These 161 * are not included in the JSON value of this record when it 162 * is generated. 163 * @return a map of any unknown attributes in the deserialized JSON. 164 */ 165 @JsonAnyGetter 166 public Map<String, String> attributes() { 167 return attributes; 168 } 169 170 /** 171 * Get the "other" attribute with a specific key 172 * @param key key to look up 173 * @return the value or null 174 */ 175 public String get(String key) { 176 return attributes.get(key); 177 } 178 179 /** 180 * Get the "other" attribute with a specific key. 181 * @param key key to look up 182 * @param defVal default value 183 * @return the value as a string, 184 * or <code>defval</code> if the value was not present 185 */ 186 public String get(String key, String defVal) { 187 String val = attributes.get(key); 188 return val != null ? val: defVal; 189 } 190 191 /** 192 * Find an endpoint by its API 193 * @param list list 194 * @param api api name 195 * @return the endpoint or null if there was no match 196 */ 197 private Endpoint findByAPI(List<Endpoint> list, String api) { 198 for (Endpoint endpoint : list) { 199 if (endpoint.api.equals(api)) { 200 return endpoint; 201 } 202 } 203 return null; 204 } 205 206 @Override 207 public String toString() { 208 final StringBuilder sb = 209 new StringBuilder("ServiceRecord{"); 210 sb.append("description='").append(description).append('\''); 211 sb.append("; external endpoints: {"); 212 for (Endpoint endpoint : external) { 213 sb.append(endpoint).append("; "); 214 } 215 sb.append("}; internal endpoints: {"); 216 for (Endpoint endpoint : internal) { 217 sb.append(endpoint != null ? endpoint.toString() : "NULL ENDPOINT"); 218 sb.append("; "); 219 } 220 sb.append('}'); 221 222 if (!attributes.isEmpty()) { 223 sb.append(", attributes: {"); 224 for (Map.Entry<String, String> attr : attributes.entrySet()) { 225 sb.append("\"").append(attr.getKey()).append("\"=\"") 226 .append(attr.getValue()).append("\" "); 227 } 228 } else { 229 230 sb.append(", attributes: {"); 231 } 232 sb.append('}'); 233 234 sb.append('}'); 235 return sb.toString(); 236 } 237 238 /** 239 * Shallow clone: all endpoints will be shared across instances 240 * @return a clone of the instance 241 * @throws CloneNotSupportedException 242 */ 243 @Override 244 protected Object clone() throws CloneNotSupportedException { 245 return super.clone(); 246 } 247 248 249}