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.binding;
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 static org.apache.hadoop.registry.client.types.AddressTypes.*;
026import org.apache.hadoop.registry.client.types.Endpoint;
027import org.apache.hadoop.registry.client.types.ProtocolTypes;
028import org.apache.hadoop.registry.client.types.ServiceRecord;
029
030import java.net.InetSocketAddress;
031import java.net.MalformedURLException;
032import java.net.URI;
033import java.net.URL;
034import java.util.ArrayList;
035import java.util.HashMap;
036import java.util.List;
037import java.util.Map;
038
039/**
040 * Static methods to work with registry types —primarily endpoints and the
041 * list representation of addresses.
042 */
043@InterfaceAudience.Public
044@InterfaceStability.Evolving
045public class RegistryTypeUtils {
046
047  /**
048   * Create a URL endpoint from a list of URIs
049   * @param api implemented API
050   * @param protocolType protocol type
051   * @param uris URIs
052   * @return a new endpoint
053   */
054  public static Endpoint urlEndpoint(String api,
055      String protocolType,
056      URI... uris) {
057    return new Endpoint(api, protocolType, uris);
058  }
059
060  /**
061   * Create a REST endpoint from a list of URIs
062   * @param api implemented API
063   * @param uris URIs
064   * @return a new endpoint
065   */
066  public static Endpoint restEndpoint(String api,
067      URI... uris) {
068    return urlEndpoint(api, ProtocolTypes.PROTOCOL_REST, uris);
069  }
070
071  /**
072   * Create a Web UI endpoint from a list of URIs
073   * @param api implemented API
074   * @param uris URIs
075   * @return a new endpoint
076   */
077  public static Endpoint webEndpoint(String api,
078      URI... uris) {
079    return urlEndpoint(api, ProtocolTypes.PROTOCOL_WEBUI, uris);
080  }
081
082  /**
083   * Create an internet address endpoint from a list of URIs
084   * @param api implemented API
085   * @param protocolType protocol type
086   * @param hostname hostname/FQDN
087   * @param port port
088   * @return a new endpoint
089   */
090
091  public static Endpoint inetAddrEndpoint(String api,
092      String protocolType,
093      String hostname,
094      int port) {
095    Preconditions.checkArgument(api != null, "null API");
096    Preconditions.checkArgument(protocolType != null, "null protocolType");
097    Preconditions.checkArgument(hostname != null, "null hostname");
098    return new Endpoint(api,
099        ADDRESS_HOSTNAME_AND_PORT,
100        protocolType,
101        hostnamePortPair(hostname, port));
102  }
103
104  /**
105   * Create an IPC endpoint
106   * @param api API
107   * @param address the address as a tuple of (hostname, port)
108   * @return the new endpoint
109   */
110  public static Endpoint ipcEndpoint(String api, InetSocketAddress address) {
111    return new Endpoint(api,
112        ADDRESS_HOSTNAME_AND_PORT,
113        ProtocolTypes.PROTOCOL_HADOOP_IPC,
114        address== null ? null: hostnamePortPair(address));
115  }
116
117  /**
118   * Create a single entry map
119   * @param key map entry key
120   * @param val map entry value
121   * @return a 1 entry map.
122   */
123  public static Map<String, String> map(String key, String val) {
124    Map<String, String> map = new HashMap<String, String>(1);
125    map.put(key, val);
126    return map;
127  }
128
129  /**
130   * Create a URI
131   * @param uri value
132   * @return a 1 entry map.
133   */
134  public static Map<String, String> uri(String uri) {
135    return map(ADDRESS_URI, uri);
136  }
137
138  /**
139   * Create a (hostname, port) address pair
140   * @param hostname hostname
141   * @param port port
142   * @return a 1 entry map.
143   */
144  public static Map<String, String> hostnamePortPair(String hostname, int port) {
145    Map<String, String> map =
146        map(ADDRESS_HOSTNAME_FIELD, hostname);
147    map.put(ADDRESS_PORT_FIELD, Integer.toString(port));
148    return map;
149  }
150
151  /**
152   * Create a (hostname, port) address pair
153   * @param address socket address whose hostname and port are used for the
154   * generated address.
155   * @return a 1 entry map.
156   */
157  public static Map<String, String> hostnamePortPair(InetSocketAddress address) {
158    return hostnamePortPair(address.getHostName(), address.getPort());
159  }
160
161  /**
162   * Require a specific address type on an endpoint
163   * @param required required type
164   * @param epr endpoint
165   * @throws InvalidRecordException if the type is wrong
166   */
167  public static void requireAddressType(String required, Endpoint epr) throws
168      InvalidRecordException {
169    if (!required.equals(epr.addressType)) {
170      throw new InvalidRecordException(
171          epr.toString(),
172          "Address type of " + epr.addressType
173          + " does not match required type of "
174          + required);
175    }
176  }
177
178  /**
179   * Get a single URI endpoint
180   * @param epr endpoint
181   * @return the uri of the first entry in the address list. Null if the endpoint
182   * itself is null
183   * @throws InvalidRecordException if the type is wrong, there are no addresses
184   * or the payload ill-formatted
185   */
186  public static List<String> retrieveAddressesUriType(Endpoint epr)
187      throws InvalidRecordException {
188    if (epr == null) {
189      return null;
190    }
191    requireAddressType(ADDRESS_URI, epr);
192    List<Map<String, String>> addresses = epr.addresses;
193    if (addresses.size() < 1) {
194      throw new InvalidRecordException(epr.toString(),
195          "No addresses in endpoint");
196    }
197    List<String> results = new ArrayList<String>(addresses.size());
198    for (Map<String, String> address : addresses) {
199      results.add(getAddressField(address, ADDRESS_URI));
200    }
201    return results;
202  }
203
204  /**
205   * Get a specific field from an address -raising an exception if
206   * the field is not present
207   * @param address address to query
208   * @param field field to resolve
209   * @return the resolved value. Guaranteed to be non-null.
210   * @throws InvalidRecordException if the field did not resolve
211   */
212  public static String getAddressField(Map<String, String> address,
213      String field) throws InvalidRecordException {
214    String val = address.get(field);
215    if (val == null) {
216      throw new InvalidRecordException("", "Missing address field: " + field);
217    }
218    return val;
219  }
220
221  /**
222   * Get the address URLs. Guranteed to return at least one address.
223   * @param epr endpoint
224   * @return the address as a URL
225   * @throws InvalidRecordException if the type is wrong, there are no addresses
226   * or the payload ill-formatted
227   * @throws MalformedURLException address can't be turned into a URL
228   */
229  public static List<URL> retrieveAddressURLs(Endpoint epr)
230      throws InvalidRecordException, MalformedURLException {
231    if (epr == null) {
232      throw new InvalidRecordException("", "Null endpoint");
233    }
234    List<String> addresses = retrieveAddressesUriType(epr);
235    List<URL> results = new ArrayList<URL>(addresses.size());
236    for (String address : addresses) {
237      results.add(new URL(address));
238    }
239    return results;
240  }
241
242  /**
243   * Validate the record by checking for null fields and other invalid
244   * conditions
245   * @param path path for exceptions
246   * @param record record to validate. May be null
247   * @throws InvalidRecordException on invalid entries
248   */
249  public static void validateServiceRecord(String path, ServiceRecord record)
250      throws InvalidRecordException {
251    if (record == null) {
252      throw new InvalidRecordException(path, "Null record");
253    }
254    if (!ServiceRecord.RECORD_TYPE.equals(record.type)) {
255      throw new InvalidRecordException(path,
256          "invalid record type field: \"" + record.type + "\"");
257    }
258
259    if (record.external != null) {
260      for (Endpoint endpoint : record.external) {
261        validateEndpoint(path, endpoint);
262      }
263    }
264    if (record.internal != null) {
265      for (Endpoint endpoint : record.internal) {
266        validateEndpoint(path, endpoint);
267      }
268    }
269  }
270
271  /**
272   * Validate the endpoint by checking for null fields and other invalid
273   * conditions
274   * @param path path for exceptions
275   * @param endpoint endpoint to validate. May be null
276   * @throws InvalidRecordException on invalid entries
277   */
278  public static void validateEndpoint(String path, Endpoint endpoint)
279      throws InvalidRecordException {
280    if (endpoint == null) {
281      throw new InvalidRecordException(path, "Null endpoint");
282    }
283    try {
284      endpoint.validate();
285    } catch (RuntimeException e) {
286      throw new InvalidRecordException(path, e.toString());
287    }
288  }
289
290}