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
019 package org.apache.hadoop.registry.client.types;
020
021 import com.google.common.base.Preconditions;
022 import org.apache.hadoop.classification.InterfaceAudience;
023 import org.apache.hadoop.classification.InterfaceStability;
024 import org.apache.hadoop.registry.client.exceptions.InvalidRecordException;
025 import org.codehaus.jackson.annotate.JsonAnyGetter;
026 import org.codehaus.jackson.annotate.JsonAnySetter;
027 import org.codehaus.jackson.map.annotate.JsonSerialize;
028
029 import java.util.ArrayList;
030 import java.util.HashMap;
031 import java.util.List;
032 import 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)
042 public 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 }