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.binding.JsonSerDeser; 025import org.apache.hadoop.registry.client.binding.RegistryTypeUtils; 026import org.codehaus.jackson.annotate.JsonIgnoreProperties; 027import org.codehaus.jackson.map.annotate.JsonSerialize; 028 029import java.net.URI; 030import java.util.ArrayList; 031import java.util.HashMap; 032import java.util.List; 033import 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) 050public 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}