blob: e4effb42c8664c34e01451f7de56d601c1219cb9 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.hadoop.registry.client.types;
import com.google.common.base.Preconditions;
import org.apache.hadoop.classification.InterfaceAudience;
import org.apache.hadoop.classification.InterfaceStability;
import org.apache.hadoop.registry.client.binding.JsonSerDeser;
import org.apache.hadoop.registry.client.binding.RegistryTypeUtils;
import org.codehaus.jackson.annotate.JsonIgnoreProperties;
import org.codehaus.jackson.map.annotate.JsonSerialize;
import java.net.URI;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* Description of a single service/component endpoint.
* It is designed to be marshalled as JSON.
* <p>
* Every endpoint can have more than one address entry, such as
* a list of URLs to a replicated service, or a (hostname, port)
* pair. Each of these address entries is represented as a string list,
* as that is the only reliably marshallable form of a tuple JSON can represent.
*
*
*/
@InterfaceAudience.Public
@InterfaceStability.Evolving
@JsonIgnoreProperties(ignoreUnknown = true)
@JsonSerialize(include = JsonSerialize.Inclusion.NON_NULL)
public final class Endpoint implements Cloneable {
/**
* API implemented at the end of the binding
*/
public String api;
/**
* Type of address. The standard types are defined in
* {@link AddressTypes}
*/
public String addressType;
/**
* Protocol type. Some standard types are defined in
* {@link ProtocolTypes}
*/
public String protocolType;
/**
* a list of address tuples —tuples whose format depends on the address type
*/
public List<Map<String, String>> addresses;
/**
* Create an empty instance.
*/
public Endpoint() {
}
/**
* Create an endpoint from another endpoint.
* This is a deep clone with a new list of addresses.
* @param that the endpoint to copy from
*/
public Endpoint(Endpoint that) {
this.api = that.api;
this.addressType = that.addressType;
this.protocolType = that.protocolType;
this.addresses = newAddresses(that.addresses.size());
for (Map<String, String> address : that.addresses) {
Map<String, String> addr2 = new HashMap<String, String>(address.size());
addr2.putAll(address);
addresses.add(addr2);
}
}
/**
* Build an endpoint with a list of addresses
* @param api API name
* @param addressType address type
* @param protocolType protocol type
* @param addrs addresses
*/
public Endpoint(String api,
String addressType,
String protocolType,
List<Map<String, String>> addrs) {
this.api = api;
this.addressType = addressType;
this.protocolType = protocolType;
this.addresses = newAddresses(0);
if (addrs != null) {
addresses.addAll(addrs);
}
}
/**
* Build an endpoint with an empty address list
* @param api API name
* @param addressType address type
* @param protocolType protocol type
*/
public Endpoint(String api,
String addressType,
String protocolType) {
this.api = api;
this.addressType = addressType;
this.protocolType = protocolType;
this.addresses = newAddresses(0);
}
/**
* Build an endpoint with a single address entry.
* <p>
* This constructor is superfluous given the varags constructor is equivalent
* for a single element argument. However, type-erasure in java generics
* causes javac to warn about unchecked generic array creation. This
* constructor, which represents the common "one address" case, does
* not generate compile-time warnings.
* @param api API name
* @param addressType address type
* @param protocolType protocol type
* @param addr address. May be null —in which case it is not added
*/
public Endpoint(String api,
String addressType,
String protocolType,
Map<String, String> addr) {
this(api, addressType, protocolType);
if (addr != null) {
addresses.add(addr);
}
}
/**
* Build an endpoint with a list of addresses
* @param api API name
* @param addressType address type
* @param protocolType protocol type
* @param addrs addresses. Null elements will be skipped
*/
public Endpoint(String api,
String addressType,
String protocolType,
Map<String, String>...addrs) {
this(api, addressType, protocolType);
for (Map<String, String> addr : addrs) {
if (addr!=null) {
addresses.add(addr);
}
}
}
/**
* Create a new address structure of the requested size
* @param size size to create
* @return the new list
*/
private List<Map<String, String>> newAddresses(int size) {
return new ArrayList<Map<String, String>>(size);
}
/**
* Build an endpoint from a list of URIs; each URI
* is ASCII-encoded and added to the list of addresses.
* @param api API name
* @param protocolType protocol type
* @param uris URIs to convert to a list of tup;les
*/
public Endpoint(String api,
String protocolType,
URI... uris) {
this.api = api;
this.addressType = AddressTypes.ADDRESS_URI;
this.protocolType = protocolType;
List<Map<String, String>> addrs = newAddresses(uris.length);
for (URI uri : uris) {
addrs.add(RegistryTypeUtils.uri(uri.toString()));
}
this.addresses = addrs;
}
@Override
public String toString() {
return marshalToString.toString(this);
}
/**
* Validate the record by checking for null fields and other invalid
* conditions
* @throws NullPointerException if a field is null when it
* MUST be set.
* @throws RuntimeException on invalid entries
*/
public void validate() {
Preconditions.checkNotNull(api, "null API field");
Preconditions.checkNotNull(addressType, "null addressType field");
Preconditions.checkNotNull(protocolType, "null protocolType field");
Preconditions.checkNotNull(addresses, "null addresses field");
for (Map<String, String> address : addresses) {
Preconditions.checkNotNull(address, "null element in address");
}
}
/**
* Shallow clone: the lists of addresses are shared
* @return a cloned instance
* @throws CloneNotSupportedException
*/
@Override
public Object clone() throws CloneNotSupportedException {
return super.clone();
}
/**
* Static instance of service record marshalling
*/
private static class Marshal extends JsonSerDeser<Endpoint> {
private Marshal() {
super(Endpoint.class);
}
}
private static final Marshal marshalToString = new Marshal();
}