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.binding.JsonSerDeser;
025 import org.apache.hadoop.registry.client.binding.RegistryTypeUtils;
026 import org.codehaus.jackson.annotate.JsonIgnoreProperties;
027 import org.codehaus.jackson.map.annotate.JsonSerialize;
028
029 import java.net.URI;
030 import java.util.ArrayList;
031 import java.util.HashMap;
032 import java.util.List;
033 import java.util.Map;
034
035 /**
036 * Description of a single service/component endpoint.
037 * It is designed to be marshalled as JSON.
038 * <p>
039 * Every endpoint can have more than one address entry, such as
040 * a list of URLs to a replicated service, or a (hostname, port)
041 * pair. Each of these address entries is represented as a string list,
042 * as that is the only reliably marshallable form of a tuple JSON can represent.
043 *
044 *
045 */
046 @InterfaceAudience.Public
047 @InterfaceStability.Evolving
048 @JsonIgnoreProperties(ignoreUnknown = true)
049 @JsonSerialize(include = JsonSerialize.Inclusion.NON_NULL)
050 public final class Endpoint implements Cloneable {
051
052 /**
053 * API implemented at the end of the binding
054 */
055 public String api;
056
057 /**
058 * Type of address. The standard types are defined in
059 * {@link AddressTypes}
060 */
061 public String addressType;
062
063 /**
064 * Protocol type. Some standard types are defined in
065 * {@link ProtocolTypes}
066 */
067 public String protocolType;
068
069 /**
070 * a list of address tuples —tuples whose format depends on the address type
071 */
072 public List<Map<String, String>> addresses;
073
074 /**
075 * Create an empty instance.
076 */
077 public Endpoint() {
078 }
079
080 /**
081 * Create an endpoint from another endpoint.
082 * This is a deep clone with a new list of addresses.
083 * @param that the endpoint to copy from
084 */
085 public Endpoint(Endpoint that) {
086 this.api = that.api;
087 this.addressType = that.addressType;
088 this.protocolType = that.protocolType;
089 this.addresses = newAddresses(that.addresses.size());
090 for (Map<String, String> address : that.addresses) {
091 Map<String, String> addr2 = new HashMap<String, String>(address.size());
092 addr2.putAll(address);
093 addresses.add(addr2);
094 }
095 }
096
097 /**
098 * Build an endpoint with a list of addresses
099 * @param api API name
100 * @param addressType address type
101 * @param protocolType protocol type
102 * @param addrs addresses
103 */
104 public Endpoint(String api,
105 String addressType,
106 String protocolType,
107 List<Map<String, String>> addrs) {
108 this.api = api;
109 this.addressType = addressType;
110 this.protocolType = protocolType;
111 this.addresses = newAddresses(0);
112 if (addrs != null) {
113 addresses.addAll(addrs);
114 }
115 }
116
117 /**
118 * Build an endpoint with an empty address list
119 * @param api API name
120 * @param addressType address type
121 * @param protocolType protocol type
122 */
123 public Endpoint(String api,
124 String addressType,
125 String protocolType) {
126 this.api = api;
127 this.addressType = addressType;
128 this.protocolType = protocolType;
129 this.addresses = newAddresses(0);
130 }
131
132 /**
133 * Build an endpoint with a single address entry.
134 * <p>
135 * This constructor is superfluous given the varags constructor is equivalent
136 * for a single element argument. However, type-erasure in java generics
137 * causes javac to warn about unchecked generic array creation. This
138 * constructor, which represents the common "one address" case, does
139 * not generate compile-time warnings.
140 * @param api API name
141 * @param addressType address type
142 * @param protocolType protocol type
143 * @param addr address. May be null —in which case it is not added
144 */
145 public Endpoint(String api,
146 String addressType,
147 String protocolType,
148 Map<String, String> addr) {
149 this(api, addressType, protocolType);
150 if (addr != null) {
151 addresses.add(addr);
152 }
153 }
154
155 /**
156 * Build an endpoint with a list of addresses
157 * @param api API name
158 * @param addressType address type
159 * @param protocolType protocol type
160 * @param addrs addresses. Null elements will be skipped
161 */
162 public Endpoint(String api,
163 String addressType,
164 String protocolType,
165 Map<String, String>...addrs) {
166 this(api, addressType, protocolType);
167 for (Map<String, String> addr : addrs) {
168 if (addr!=null) {
169 addresses.add(addr);
170 }
171 }
172 }
173
174 /**
175 * Create a new address structure of the requested size
176 * @param size size to create
177 * @return the new list
178 */
179 private List<Map<String, String>> newAddresses(int size) {
180 return new ArrayList<Map<String, String>>(size);
181 }
182
183 /**
184 * Build an endpoint from a list of URIs; each URI
185 * is ASCII-encoded and added to the list of addresses.
186 * @param api API name
187 * @param protocolType protocol type
188 * @param uris URIs to convert to a list of tup;les
189 */
190 public Endpoint(String api,
191 String protocolType,
192 URI... uris) {
193 this.api = api;
194 this.addressType = AddressTypes.ADDRESS_URI;
195
196 this.protocolType = protocolType;
197 List<Map<String, String>> addrs = newAddresses(uris.length);
198 for (URI uri : uris) {
199 addrs.add(RegistryTypeUtils.uri(uri.toString()));
200 }
201 this.addresses = addrs;
202 }
203
204 @Override
205 public String toString() {
206 return marshalToString.toString(this);
207 }
208
209 /**
210 * Validate the record by checking for null fields and other invalid
211 * conditions
212 * @throws NullPointerException if a field is null when it
213 * MUST be set.
214 * @throws RuntimeException on invalid entries
215 */
216 public void validate() {
217 Preconditions.checkNotNull(api, "null API field");
218 Preconditions.checkNotNull(addressType, "null addressType field");
219 Preconditions.checkNotNull(protocolType, "null protocolType field");
220 Preconditions.checkNotNull(addresses, "null addresses field");
221 for (Map<String, String> address : addresses) {
222 Preconditions.checkNotNull(address, "null element in address");
223 }
224 }
225
226 /**
227 * Shallow clone: the lists of addresses are shared
228 * @return a cloned instance
229 * @throws CloneNotSupportedException
230 */
231 @Override
232 public Object clone() throws CloneNotSupportedException {
233 return super.clone();
234 }
235
236
237 /**
238 * Static instance of service record marshalling
239 */
240 private static class Marshal extends JsonSerDeser<Endpoint> {
241 private Marshal() {
242 super(Endpoint.class);
243 }
244 }
245
246 private static final Marshal marshalToString = new Marshal();
247 }