/* * Copyright 2012 Netflix, Inc. * * Licensed 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 com.netflix.appinfo; import java.util.Collections; import java.util.HashMap; import java.util.LinkedHashSet; import java.util.Locale; import java.util.Map; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import com.fasterxml.jackson.annotation.JsonCreator; import com.fasterxml.jackson.annotation.JsonIgnore; import com.fasterxml.jackson.annotation.JsonProperty; import com.fasterxml.jackson.annotation.JsonRootName; import com.google.inject.ProvidedBy; import com.netflix.appinfo.providers.Archaius1VipAddressResolver; import com.netflix.appinfo.providers.EurekaConfigBasedInstanceInfoProvider; import com.netflix.appinfo.providers.VipAddressResolver; import com.netflix.discovery.converters.Auto; import com.netflix.discovery.converters.EurekaJacksonCodec.InstanceInfoSerializer; import com.netflix.discovery.provider.Serializer; import com.netflix.discovery.util.StringCache; import com.thoughtworks.xstream.annotations.XStreamAlias; import com.thoughtworks.xstream.annotations.XStreamOmitField; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * The class that holds information required for registration with * <tt>Eureka Server</tt> and to be discovered by other components. * <p> * <code>@Auto</code> annotated fields are serialized as is; Other fields are * serialized as specified by the <code>@Serializer</code>. * </p> * * @author Karthik Ranganathan, Greg Kim */ @ProvidedBy(EurekaConfigBasedInstanceInfoProvider.class) @Serializer("com.netflix.discovery.converters.EntityBodyConverter") @XStreamAlias("instance") @JsonRootName("instance") public class InstanceInfo { /** * {@link InstanceInfo} JSON and XML format for port information does not follow the usual conventions, which * makes its mapping complicated. This class represents the wire format for port information. */ public static class PortWrapper { private final boolean enabled; private final int port; @JsonCreator public PortWrapper(@JsonProperty("@enabled") boolean enabled, @JsonProperty("$") int port) { this.enabled = enabled; this.port = port; } public boolean isEnabled() { return enabled; } public int getPort() { return port; } } private static final Logger logger = LoggerFactory.getLogger(InstanceInfo.class); public static final int DEFAULT_PORT = 7001; public static final int DEFAULT_SECURE_PORT = 7002; public static final int DEFAULT_COUNTRY_ID = 1; // US // The (fixed) instanceId for this instanceInfo. This should be unique within the scope of the appName. private volatile String instanceId; private volatile String appName; @Auto private volatile String appGroupName; private volatile String ipAddr; private static final String SID_DEFAULT = "na"; @Deprecated private volatile String sid = SID_DEFAULT; private volatile int port = DEFAULT_PORT; private volatile int securePort = DEFAULT_SECURE_PORT; @Auto private volatile String homePageUrl; @Auto private volatile String statusPageUrl; @Auto private volatile String healthCheckUrl; @Auto private volatile String secureHealthCheckUrl; @Auto private volatile String vipAddress; @Auto private volatile String secureVipAddress; @XStreamOmitField private String statusPageRelativeUrl; @XStreamOmitField private String statusPageExplicitUrl; @XStreamOmitField private String healthCheckRelativeUrl; @XStreamOmitField private String healthCheckSecureExplicitUrl; @XStreamOmitField private String vipAddressUnresolved; @XStreamOmitField private String secureVipAddressUnresolved; @XStreamOmitField private String healthCheckExplicitUrl; @Deprecated private volatile int countryId = DEFAULT_COUNTRY_ID; // Defaults to US private volatile boolean isSecurePortEnabled = false; private volatile boolean isUnsecurePortEnabled = true; private volatile DataCenterInfo dataCenterInfo; private volatile String hostName; private volatile InstanceStatus status = InstanceStatus.UP; private volatile InstanceStatus overriddenstatus = InstanceStatus.UNKNOWN; @XStreamOmitField private volatile boolean isInstanceInfoDirty = false; private volatile LeaseInfo leaseInfo; @Auto private volatile Boolean isCoordinatingDiscoveryServer = Boolean.FALSE; @XStreamAlias("metadata") private volatile Map<String, String> metadata = new ConcurrentHashMap<String, String>(); @Auto private volatile Long lastUpdatedTimestamp = System.currentTimeMillis(); @Auto private volatile Long lastDirtyTimestamp = System.currentTimeMillis(); @Auto private volatile ActionType actionType; @Auto private volatile String asgName; private String version = "unknown"; private InstanceInfo() { } @JsonCreator public InstanceInfo( @JsonProperty("instanceId") String instanceId, @JsonProperty("app") String appName, @JsonProperty("appGroupName") String appGroupName, @JsonProperty("ipAddr") String ipAddr, @JsonProperty("sid") String sid, @JsonProperty("port") PortWrapper port, @JsonProperty("securePort") PortWrapper securePort, @JsonProperty("homePageUrl") String homePageUrl, @JsonProperty("statusPageUrl") String statusPageUrl, @JsonProperty("healthCheckUrl") String healthCheckUrl, @JsonProperty("secureHealthCheckUrl") String secureHealthCheckUrl, @JsonProperty("vipAddress") String vipAddress, @JsonProperty("secureVipAddress") String secureVipAddress, @JsonProperty("countryId") int countryId, @JsonProperty("dataCenterInfo") DataCenterInfo dataCenterInfo, @JsonProperty("hostName") String hostName, @JsonProperty("status") InstanceStatus status, @JsonProperty("overriddenstatus") InstanceStatus overriddenstatus, @JsonProperty("leaseInfo") LeaseInfo leaseInfo, @JsonProperty("isCoordinatingDiscoveryServer") Boolean isCoordinatingDiscoveryServer, @JsonProperty("metadata") HashMap<String, String> metadata, @JsonProperty("lastUpdatedTimestamp") Long lastUpdatedTimestamp, @JsonProperty("lastDirtyTimestamp") Long lastDirtyTimestamp, @JsonProperty("actionType") ActionType actionType, @JsonProperty("asgName") String asgName) { this.instanceId = instanceId; this.sid = sid; this.appName = StringCache.intern(appName); this.appGroupName = StringCache.intern(appGroupName); this.ipAddr = ipAddr; this.port = port == null ? 0 : port.getPort(); this.isUnsecurePortEnabled = port != null && port.isEnabled(); this.securePort = securePort == null ? 0 : securePort.getPort(); this.isSecurePortEnabled = securePort != null && securePort.isEnabled(); this.homePageUrl = homePageUrl; this.statusPageUrl = statusPageUrl; this.healthCheckUrl = healthCheckUrl; this.secureHealthCheckUrl = secureHealthCheckUrl; this.vipAddress = StringCache.intern(vipAddress); this.secureVipAddress = StringCache.intern(secureVipAddress); this.countryId = countryId; this.dataCenterInfo = dataCenterInfo; this.hostName = hostName; this.status = status; this.overriddenstatus = overriddenstatus; this.leaseInfo = leaseInfo; this.isCoordinatingDiscoveryServer = isCoordinatingDiscoveryServer; this.lastUpdatedTimestamp = lastUpdatedTimestamp; this.lastDirtyTimestamp = lastDirtyTimestamp; this.actionType = actionType; this.asgName = StringCache.intern(asgName); // --------------------------------------------------------------- // for compatibility if (metadata == null) { this.metadata = Collections.emptyMap(); } else if (metadata.size() == 1) { this.metadata = removeMetadataMapLegacyValues(metadata); } else { this.metadata = metadata; } if (sid == null) { this.sid = SID_DEFAULT; } } private Map<String, String> removeMetadataMapLegacyValues(Map<String, String> metadata) { if (InstanceInfoSerializer.METADATA_COMPATIBILITY_VALUE.equals(metadata.get(InstanceInfoSerializer.METADATA_COMPATIBILITY_KEY))) { // TODO this else if can be removed once the server no longer uses legacy json metadata.remove(InstanceInfoSerializer.METADATA_COMPATIBILITY_KEY); } else if (InstanceInfoSerializer.METADATA_COMPATIBILITY_VALUE.equals(metadata.get("class"))) { // TODO this else if can be removed once the server no longer uses legacy xml metadata.remove("class"); } return metadata; } /** * shallow copy constructor. * * @param ii The object to copy */ public InstanceInfo(InstanceInfo ii) { this.instanceId = ii.instanceId; this.appName = ii.appName; this.appGroupName = ii.appGroupName; this.ipAddr = ii.ipAddr; this.sid = ii.sid; this.port = ii.port; this.securePort = ii.securePort; this.homePageUrl = ii.homePageUrl; this.statusPageUrl = ii.statusPageUrl; this.healthCheckUrl = ii.healthCheckUrl; this.secureHealthCheckUrl = ii.secureHealthCheckUrl; this.vipAddress = ii.vipAddress; this.secureVipAddress = ii.secureVipAddress; this.statusPageRelativeUrl = ii.statusPageRelativeUrl; this.statusPageExplicitUrl = ii.statusPageExplicitUrl; this.healthCheckRelativeUrl = ii.healthCheckRelativeUrl; this.healthCheckSecureExplicitUrl = ii.healthCheckSecureExplicitUrl; this.vipAddressUnresolved = ii.vipAddressUnresolved; this.secureVipAddressUnresolved = ii.secureVipAddressUnresolved; this.healthCheckExplicitUrl = ii.healthCheckExplicitUrl; this.countryId = ii.countryId; this.isSecurePortEnabled = ii.isSecurePortEnabled; this.isUnsecurePortEnabled = ii.isUnsecurePortEnabled; this.dataCenterInfo = ii.dataCenterInfo; this.hostName = ii.hostName; this.status = ii.status; this.overriddenstatus = ii.overriddenstatus; this.isInstanceInfoDirty = ii.isInstanceInfoDirty; this.leaseInfo = ii.leaseInfo; this.isCoordinatingDiscoveryServer = ii.isCoordinatingDiscoveryServer; this.metadata = ii.metadata; this.lastUpdatedTimestamp = ii.lastUpdatedTimestamp; this.lastDirtyTimestamp = ii.lastDirtyTimestamp; this.actionType = ii.actionType; this.asgName = ii.asgName; this.version = ii.version; } public enum InstanceStatus { UP, // Ready to receive traffic DOWN, // Do not send traffic- healthcheck callback failed STARTING, // Just about starting- initializations to be done - do not // send traffic OUT_OF_SERVICE, // Intentionally shutdown for traffic UNKNOWN; public static InstanceStatus toEnum(String s) { for (InstanceStatus e : InstanceStatus.values()) { if (e.name().equalsIgnoreCase(s)) { return e; } } return UNKNOWN; } } @Override public int hashCode() { final int prime = 31; int result = 1; result = prime * result + ((getId() == null) ? 0 : getId().hashCode()); return result; } @Override public boolean equals(Object obj) { if (this == obj) { return true; } if (obj == null) { return false; } if (getClass() != obj.getClass()) { return false; } InstanceInfo other = (InstanceInfo) obj; if (getId() == null) { if (other.getId() != null) { return false; } } else if (!getId().equals(other.getId())) { return false; } return true; } public enum PortType { SECURE, UNSECURE } public static final class Builder { private static final String COLON = ":"; private static final String HTTPS_PROTOCOL = "https://"; private static final String HTTP_PROTOCOL = "http://"; private static final class LazyHolder { private static final VipAddressResolver DEFAULT_VIP_ADDRESS_RESOLVER = new Archaius1VipAddressResolver(); } @XStreamOmitField private InstanceInfo result; @XStreamOmitField private final VipAddressResolver vipAddressResolver; private String namespace; private Builder(InstanceInfo result, VipAddressResolver vipAddressResolver) { this.vipAddressResolver = vipAddressResolver; this.result = result; } public Builder(InstanceInfo instanceInfo) { this(instanceInfo, LazyHolder.DEFAULT_VIP_ADDRESS_RESOLVER); } public static Builder newBuilder() { return new Builder(new InstanceInfo(), LazyHolder.DEFAULT_VIP_ADDRESS_RESOLVER); } public static Builder newBuilder(VipAddressResolver vipAddressResolver) { return new Builder(new InstanceInfo(), vipAddressResolver); } public Builder setInstanceId(String instanceId) { result.instanceId = instanceId; return this; } /** * Set the application name of the instance.This is mostly used in * querying of instances. * * @param appName the application name. * @return the instance info builder. */ public Builder setAppName(String appName) { result.appName = StringCache.intern(appName.toUpperCase(Locale.ROOT)); return this; } public Builder setAppGroupName(String appGroupName) { if (appGroupName != null) { result.appGroupName = appGroupName.toUpperCase(Locale.ROOT); } else { result.appGroupName = null; } return this; } /** * Sets the fully qualified hostname of this running instance.This is * mostly used in constructing the {@link java.net.URL} for communicating with * the instance. * * @param hostName the host name of the instance. * @return the {@link InstanceInfo} builder. */ public Builder setHostName(String hostName) { if (hostName == null || hostName.isEmpty()) { logger.warn("Passed in hostname is blank, not setting it"); return this; } String existingHostName = result.hostName; result.hostName = hostName; if ((existingHostName != null) && !(hostName.equals(existingHostName))) { refreshStatusPageUrl().refreshHealthCheckUrl() .refreshVIPAddress().refreshSecureVIPAddress(); } return this; } /** * Sets the status of the instances.If the status is UP, that is when * the instance is ready to service requests. * * @param status the {@link InstanceStatus} of the instance. * @return the {@link InstanceInfo} builder. */ public Builder setStatus(InstanceStatus status) { result.status = status; return this; } /** * Sets the status overridden by some other external process.This is * mostly used in putting an instance out of service to block traffic to * it. * * @param status the overridden {@link InstanceStatus} of the instance. * @return @return the {@link InstanceInfo} builder. */ public Builder setOverriddenStatus(InstanceStatus status) { result.overriddenstatus = status; return this; } /** * Sets the ip address of this running instance. * * @param ip the ip address of the instance. * @return the {@link InstanceInfo} builder. */ public Builder setIPAddr(String ip) { result.ipAddr = ip; return this; } /** * Sets the identity of this application instance. * * @param sid the sid of the instance. * @return the {@link InstanceInfo} builder. */ @Deprecated public Builder setSID(String sid) { result.sid = sid; return this; } /** * Sets the port number that is used to service requests. * * @param port the port number that is used to service requests. * @return the {@link InstanceInfo} builder. */ public Builder setPort(int port) { result.port = port; return this; } /** * Sets the secure port that is used to service requests. * * @param port the secure port that is used to service requests. * @return the {@link InstanceInfo} builder. */ public Builder setSecurePort(int port) { result.securePort = port; return this; } /** * Enabled or disable secure/non-secure ports . * * @param type Secure or Non-Secure. * @param isEnabled true if enabled. * @return the instance builder. */ public Builder enablePort(PortType type, boolean isEnabled) { if (type == PortType.SECURE) { result.isSecurePortEnabled = isEnabled; } else { result.isUnsecurePortEnabled = isEnabled; } return this; } @Deprecated public Builder setCountryId(int id) { result.countryId = id; return this; } /** * Sets the absolute home page {@link java.net.URL} for this instance. The users * can provide the <code>homePageUrlPath</code> if the home page resides * in the same instance talking to discovery, else in the cases where * the instance is a proxy for some other server, it can provide the * full {@link java.net.URL}. If the full {@link java.net.URL} is provided it takes * precedence. * <p> * <p> * The full {@link java.net.URL} should follow the format * http://${netflix.appinfo.hostname}:7001/ where the value * ${netflix.appinfo.hostname} is replaced at runtime. * </p> * * @param relativeUrl the relative url path of the home page. * @param explicitUrl - The full {@link java.net.URL} for the home page * @return the instance builder. */ public Builder setHomePageUrl(String relativeUrl, String explicitUrl) { String hostNameInterpolationExpression = "${" + namespace + "hostname}"; if (explicitUrl != null) { result.homePageUrl = explicitUrl.replace( hostNameInterpolationExpression, result.hostName); } else if (relativeUrl != null) { result.homePageUrl = HTTP_PROTOCOL + result.hostName + COLON + result.port + relativeUrl; } return this; } /** * {@link #setHomePageUrl(String, String)} has complex logic that should not be invoked when * we deserialize {@link InstanceInfo} object, or home page URL is formatted already by the client. */ public Builder setHomePageUrlForDeser(String homePageUrl) { result.homePageUrl = homePageUrl; return this; } /** * Sets the absolute status page {@link java.net.URL} for this instance. The * users can provide the <code>statusPageUrlPath</code> if the status * page resides in the same instance talking to discovery, else in the * cases where the instance is a proxy for some other server, it can * provide the full {@link java.net.URL}. If the full {@link java.net.URL} is provided it * takes precedence. * <p> * <p> * The full {@link java.net.URL} should follow the format * http://${netflix.appinfo.hostname}:7001/Status where the value * ${netflix.appinfo.hostname} is replaced at runtime. * </p> * * @param relativeUrl - The {@link java.net.URL} path for status page for this instance * @param explicitUrl - The full {@link java.net.URL} for the status page * @return - Builder instance */ public Builder setStatusPageUrl(String relativeUrl, String explicitUrl) { String hostNameInterpolationExpression = "${" + namespace + "hostname}"; result.statusPageRelativeUrl = relativeUrl; result.statusPageExplicitUrl = explicitUrl; if (explicitUrl != null) { result.statusPageUrl = explicitUrl.replace( hostNameInterpolationExpression, result.hostName); } else if (relativeUrl != null) { result.statusPageUrl = HTTP_PROTOCOL + result.hostName + COLON + result.port + relativeUrl; } return this; } /** * {@link #setStatusPageUrl(String, String)} has complex logic that should not be invoked when * we deserialize {@link InstanceInfo} object, or status page URL is formatted already by the client. */ public Builder setStatusPageUrlForDeser(String statusPageUrl) { result.statusPageUrl = statusPageUrl; return this; } /** * Sets the absolute health check {@link java.net.URL} for this instance for both * secure and non-secure communication The users can provide the * <code>healthCheckUrlPath</code> if the healthcheck page resides in * the same instance talking to discovery, else in the cases where the * instance is a proxy for some other server, it can provide the full * {@link java.net.URL}. If the full {@link java.net.URL} is provided it takes precedence. * <p> * <p> * The full {@link java.net.URL} should follow the format * http://${netflix.appinfo.hostname}:7001/healthcheck where the value * ${netflix.appinfo.hostname} is replaced at runtime. * </p> * * @param relativeUrl - The {@link java.net.URL} path for healthcheck page for this * instance. * @param explicitUrl - The full {@link java.net.URL} for the healthcheck page. * @param secureExplicitUrl the full secure explicit url of the healthcheck page. * @return the instance builder */ public Builder setHealthCheckUrls(String relativeUrl, String explicitUrl, String secureExplicitUrl) { String hostNameInterpolationExpression = "${" + namespace + "hostname}"; result.healthCheckRelativeUrl = relativeUrl; result.healthCheckExplicitUrl = explicitUrl; result.healthCheckSecureExplicitUrl = secureExplicitUrl; if (explicitUrl != null) { result.healthCheckUrl = explicitUrl.replace( hostNameInterpolationExpression, result.hostName); } else if (result.isUnsecurePortEnabled) { result.healthCheckUrl = HTTP_PROTOCOL + result.hostName + COLON + result.port + relativeUrl; } if (secureExplicitUrl != null) { result.secureHealthCheckUrl = secureExplicitUrl.replace( hostNameInterpolationExpression, result.hostName); } else if (result.isSecurePortEnabled) { result.secureHealthCheckUrl = HTTPS_PROTOCOL + result.hostName + COLON + result.securePort + relativeUrl; } return this; } /** * {@link #setHealthCheckUrls(String, String, String)} has complex logic that should not be invoked when * we deserialize {@link InstanceInfo} object, or health check URLs are formatted already by the client. */ public Builder setHealthCheckUrlsForDeser(String healthCheckUrl, String secureHealthCheckUrl) { if (healthCheckUrl != null) { result.healthCheckUrl = healthCheckUrl; } if (secureHealthCheckUrl != null) { result.secureHealthCheckUrl = secureHealthCheckUrl; } return this; } /** * Sets the Virtual Internet Protocol address for this instance. The * address should follow the format <code><hostname:port></code> This * address needs to be resolved into a real address for communicating * with this instance. * * @param vipAddress - The Virtual Internet Protocol address of this instance. * @return the instance builder. */ public Builder setVIPAddress(final String vipAddress) { result.vipAddressUnresolved = StringCache.intern(vipAddress); result.vipAddress = StringCache.intern( vipAddressResolver.resolveDeploymentContextBasedVipAddresses(vipAddress)); return this; } /** * Setter used during deserialization process, that does not do macro expansion on the provided value. */ public Builder setVIPAddressDeser(String vipAddress) { result.vipAddress = StringCache.intern(vipAddress); return this; } /** * Sets the Secure Virtual Internet Protocol address for this instance. * The address should follow the format <hostname:port> This address * needs to be resolved into a real address for communicating with this * instance. * * @param secureVIPAddress the secure VIP address of the instance. * @return - Builder instance */ public Builder setSecureVIPAddress(final String secureVIPAddress) { result.secureVipAddressUnresolved = StringCache.intern(secureVIPAddress); result.secureVipAddress = StringCache.intern( vipAddressResolver.resolveDeploymentContextBasedVipAddresses(secureVIPAddress)); return this; } /** * Setter used during deserialization process, that does not do macro expansion on the provided value. */ public Builder setSecureVIPAddressDeser(String secureVIPAddress) { result.secureVipAddress = StringCache.intern(secureVIPAddress); return this; } /** * Sets the datacenter information. * * @param datacenter the datacenter information for where this instance is * running. * @return the {@link InstanceInfo} builder. */ public Builder setDataCenterInfo(DataCenterInfo datacenter) { result.dataCenterInfo = datacenter; return this; } /** * Set the instance lease information. * * @param info the lease information for this instance. */ public Builder setLeaseInfo(LeaseInfo info) { result.leaseInfo = info; return this; } /** * Add arbitrary metadata to running instance. * * @param key The key of the metadata. * @param val The value of the metadata. * @return the {@link InstanceInfo} builder. */ public Builder add(String key, String val) { result.metadata.put(key, val); return this; } /** * Replace the existing metadata map with a new one. * * @param mt the new metadata name. * @return instance info builder. */ public Builder setMetadata(Map<String, String> mt) { result.metadata = mt; return this; } /** * Returns the encapsulated instance info even it it is not built fully. * * @return the existing information about the instance. */ public InstanceInfo getRawInstance() { return result; } /** * Build the {@link InstanceInfo} object. * * @return the {@link InstanceInfo} that was built based on the * information supplied. */ public InstanceInfo build() { if (!isInitialized()) { throw new IllegalStateException("name is required!"); } return result; } public boolean isInitialized() { return (result.appName != null); } /** * Sets the AWS ASG name for this instance. * * @param asgName the asg name for this instance. * @return the instance info builder. */ public Builder setASGName(String asgName) { result.asgName = StringCache.intern(asgName); return this; } private Builder refreshStatusPageUrl() { setStatusPageUrl(result.statusPageRelativeUrl, result.statusPageExplicitUrl); return this; } private Builder refreshHealthCheckUrl() { setHealthCheckUrls(result.healthCheckRelativeUrl, result.healthCheckExplicitUrl, result.healthCheckSecureExplicitUrl); return this; } private Builder refreshSecureVIPAddress() { setSecureVIPAddress(result.secureVipAddressUnresolved); return this; } private Builder refreshVIPAddress() { setVIPAddress(result.vipAddressUnresolved); return this; } public Builder setIsCoordinatingDiscoveryServer(boolean isCoordinatingDiscoveryServer) { result.isCoordinatingDiscoveryServer = isCoordinatingDiscoveryServer; return this; } public Builder setLastUpdatedTimestamp(long lastUpdatedTimestamp) { result.lastUpdatedTimestamp = lastUpdatedTimestamp; return this; } public Builder setLastDirtyTimestamp(long lastDirtyTimestamp) { result.lastDirtyTimestamp = lastDirtyTimestamp; return this; } public Builder setActionType(ActionType actionType) { result.actionType = actionType; return this; } public Builder setNamespace(String namespace) { this.namespace = namespace.endsWith(".") ? namespace : namespace + "."; return this; } } /** * @return the raw instanceId. For compatibility, prefer to use {@link #getId()} */ public String getInstanceId() { return instanceId; } /** * Return the name of the application registering with discovery. * * @return the string denoting the application name. */ @JsonProperty("app") public String getAppName() { return appName; } public String getAppGroupName() { return appGroupName; } /** * Return the default network address to connect to this instance. Typically this would be the fully * qualified public hostname. * * However the user can configure the {@link EurekaInstanceConfig} to change the default value used * to populate this field using the {@link EurekaInstanceConfig#getDefaultAddressResolutionOrder()} property. * * If a use case need more specific hostnames or ips, please use data from {@link #getDataCenterInfo()}. * * For legacy reasons, it is difficult to introduce a new address-type field that is agnostic to hostname/ip. * * @return the default address (by default the public hostname) */ public String getHostName() { return hostName; } @Deprecated public void setSID(String sid) { this.sid = sid; setIsDirty(); } @JsonProperty("sid") @Deprecated public String getSID() { return sid; } /** * Returns the unique id of the instance. * (Note) now that id is set at creation time within the instanceProvider, why do the other checks? * This is still necessary for backwards compatibility when upgrading in a deployment with multiple * client versions (some with the change, some without). * * @return the unique id. */ @JsonIgnore public String getId() { if (instanceId != null && !instanceId.isEmpty()) { return instanceId; } else if (dataCenterInfo instanceof UniqueIdentifier) { String uniqueId = ((UniqueIdentifier) dataCenterInfo).getId(); if (uniqueId != null && !uniqueId.isEmpty()) { return uniqueId; } } return hostName; } /** * Returns the ip address of the instance. * * @return - the ip address, in AWS scenario it is a private IP. */ @JsonProperty("ipAddr") public String getIPAddr() { return ipAddr; } /** * Returns the port number that is used for servicing requests. * * @return - the non-secure port number. */ @JsonIgnore public int getPort() { return port; } /** * Returns the status of the instance. * * @return the status indicating whether the instance can handle requests. */ public InstanceStatus getStatus() { return status; } /** * Returns the overridden status if any of the instance. * * @return the status indicating whether an external process has changed the * status. */ public InstanceStatus getOverriddenStatus() { return overriddenstatus; } /** * Returns data center information identifying if it is AWS or not. * * @return the data center information. */ public DataCenterInfo getDataCenterInfo() { return dataCenterInfo; } /** * Returns the lease information regarding when it expires. * * @return the lease information of this instance. */ public LeaseInfo getLeaseInfo() { return leaseInfo; } /** * Sets the lease information regarding when it expires. * * @param info the lease information of this instance. */ public void setLeaseInfo(LeaseInfo info) { leaseInfo = info; } /** * Returns all application specific metadata set on the instance. * * @return application specific metadata. */ public Map<String, String> getMetadata() { return metadata; } @Deprecated public int getCountryId() { return countryId; } /** * Returns the secure port that is used for servicing requests. * * @return the secure port. */ @JsonIgnore public int getSecurePort() { return securePort; } /** * Checks whether a port is enabled for traffic or not. * * @param type indicates whether it is secure or non-secure port. * @return true if the port is enabled, false otherwise. */ @JsonIgnore public boolean isPortEnabled(PortType type) { if (type == PortType.SECURE) { return isSecurePortEnabled; } else { return isUnsecurePortEnabled; } } /** * Returns the time elapsed since epoch since the instance status has been * last updated. * * @return the time elapsed since epoch since the instance has been last * updated. */ public long getLastUpdatedTimestamp() { return lastUpdatedTimestamp; } /** * Set the update time for this instance when the status was update. */ public void setLastUpdatedTimestamp() { this.lastUpdatedTimestamp = System.currentTimeMillis(); } /** * Gets the home page {@link java.net.URL} set for this instance. * * @return home page {@link java.net.URL} */ public String getHomePageUrl() { return homePageUrl; } /** * Gets the status page {@link java.net.URL} set for this instance. * * @return status page {@link java.net.URL} */ public String getStatusPageUrl() { return statusPageUrl; } /** * Gets the absolute URLs for the health check page for both secure and * non-secure protocols. If the port is not enabled then the URL is * excluded. * * @return A Set containing the string representation of health check urls * for secure and non secure protocols */ @JsonIgnore public Set<String> getHealthCheckUrls() { Set<String> healthCheckUrlSet = new LinkedHashSet<String>(); if (this.isUnsecurePortEnabled && healthCheckUrl != null && !healthCheckUrl.isEmpty()) { healthCheckUrlSet.add(healthCheckUrl); } if (this.isSecurePortEnabled && secureHealthCheckUrl != null && !secureHealthCheckUrl.isEmpty()) { healthCheckUrlSet.add(secureHealthCheckUrl); } return healthCheckUrlSet; } public String getHealthCheckUrl() { return healthCheckUrl; } public String getSecureHealthCheckUrl() { return secureHealthCheckUrl; } /** * Gets the Virtual Internet Protocol address for this instance. Defaults to * hostname if not specified. * * @return - The Virtual Internet Protocol address */ @JsonProperty("vipAddress") public String getVIPAddress() { return vipAddress; } /** * Get the Secure Virtual Internet Protocol address for this instance. * Defaults to hostname if not specified. * * @return - The Secure Virtual Internet Protocol address. */ public String getSecureVipAddress() { return secureVipAddress; } /** * Gets the last time stamp when this instance was touched. * * @return last timestamp when this instance was touched. */ public Long getLastDirtyTimestamp() { return lastDirtyTimestamp; } /** * Set the time indicating that the instance was touched. * * @param lastDirtyTimestamp time when the instance was touched. */ public void setLastDirtyTimestamp(Long lastDirtyTimestamp) { this.lastDirtyTimestamp = lastDirtyTimestamp; } /** * Set the status for this instance. * * @param status status for this instance. * @return the prev status if a different status from the current was set, null otherwise */ public synchronized InstanceStatus setStatus(InstanceStatus status) { if (this.status != status) { InstanceStatus prev = this.status; this.status = status; setIsDirty(); return prev; } return null; } /** * Set the status for this instance without updating the dirty timestamp. * * @param status status for this instance. */ public synchronized void setStatusWithoutDirty(InstanceStatus status) { if (this.status != status) { this.status = status; } } /** * Sets the overridden status for this instance.Normally set by an external * process to disable instance from taking traffic. * * @param status overridden status for this instance. */ public synchronized void setOverriddenStatus(InstanceStatus status) { if (this.overriddenstatus != status) { this.overriddenstatus = status; } } /** * Returns whether any state changed so that {@link com.netflix.discovery.EurekaClient} can * check whether to retransmit info or not on the next heartbeat. * * @return true if the {@link InstanceInfo} is dirty, false otherwise. */ @JsonIgnore public boolean isDirty() { return isInstanceInfoDirty; } /** * @return the lastDirtyTimestamp if is dirty, null otherwise. */ public synchronized Long isDirtyWithTime() { if (isInstanceInfoDirty) { return lastDirtyTimestamp; } else { return null; } } /** * @param isDirty true if dirty, false otherwise. * @deprecated use {@link #setIsDirty()} and {@link #unsetIsDirty(long)} to set and unset * <p> * Sets the dirty flag so that the instance information can be carried to * the discovery server on the next heartbeat. */ @Deprecated public synchronized void setIsDirty(boolean isDirty) { if (isDirty) { setIsDirty(); } else { isInstanceInfoDirty = false; // else don't update lastDirtyTimestamp as we are setting isDirty to false } } /** * Sets the dirty flag so that the instance information can be carried to * the discovery server on the next heartbeat. */ public synchronized void setIsDirty() { isInstanceInfoDirty = true; lastDirtyTimestamp = System.currentTimeMillis(); } /** * Unset the dirty flag iff the unsetDirtyTimestamp matches the lastDirtyTimestamp. No-op if * lastDirtyTimestamp > unsetDirtyTimestamp * * @param unsetDirtyTimestamp the expected lastDirtyTimestamp to unset. */ public synchronized void unsetIsDirty(long unsetDirtyTimestamp) { if (lastDirtyTimestamp <= unsetDirtyTimestamp) { isInstanceInfoDirty = false; } else { } } /** * Sets a flag if this instance is the same as the discovery server that is * return the instances. This flag is used by the discovery clients to * identity the discovery server which is coordinating/returning the * information. */ public void setIsCoordinatingDiscoveryServer() { String instanceId = getId(); if ((instanceId != null) && (instanceId.equals(ApplicationInfoManager.getInstance() .getInfo().getId()))) { isCoordinatingDiscoveryServer = Boolean.TRUE; } else { isCoordinatingDiscoveryServer = Boolean.FALSE; } } /** * Finds if this instance is the coordinating discovery server. * * @return - true, if this instance is the coordinating discovery server, * false otherwise. */ @JsonProperty("isCoordinatingDiscoveryServer") public Boolean isCoordinatingDiscoveryServer() { return isCoordinatingDiscoveryServer; } /** * Returns the type of action done on the instance in the server.Primarily * used for updating deltas in the {@link com.netflix.discovery.EurekaClient} * instance. * * @return action type done on the instance. */ public ActionType getActionType() { return actionType; } /** * Set the action type performed on this instance in the server. * * @param actionType action type done on the instance. */ public void setActionType(ActionType actionType) { this.actionType = actionType; } /** * Get AWS autoscaling group name if any. * * @return autoscaling group name of this instance. */ @JsonProperty("asgName") public String getASGName() { return this.asgName; } /** * Returns the specification version of this application. * * @return the string indicating the version of the application. */ @Deprecated @JsonIgnore public String getVersion() { return version; } public enum ActionType { ADDED, // Added in the discovery server MODIFIED, // Changed in the discovery server DELETED // Deleted from the discovery server } /** * Register application specific metadata to be sent to the discovery * server. * * @param runtimeMetadata * Map containing key/value pairs. */ synchronized void registerRuntimeMetadata( Map<String, String> runtimeMetadata) { metadata.putAll(runtimeMetadata); setIsDirty(); } /** * Get the zone that a particular instance is in. * Note that for AWS deployments, myInfo should contain AWS dataCenterInfo which should contain * the AWS zone of the instance, and availZones is ignored. * * @param availZones the list of available zones for non-AWS deployments * @param myInfo * - The InstanceInfo object of the instance. * @return - The zone in which the particular instance belongs to. */ public static String getZone(String[] availZones, InstanceInfo myInfo) { String instanceZone = ((availZones == null || availZones.length == 0) ? "default" : availZones[0]); if (myInfo != null && myInfo.getDataCenterInfo().getName() == DataCenterInfo.Name.Amazon) { String awsInstanceZone = ((AmazonInfo) myInfo.getDataCenterInfo()) .get(AmazonInfo.MetaDataKey.availabilityZone); if (awsInstanceZone != null) { instanceZone = awsInstanceZone; } } return instanceZone; } }