/*
* Copyright 2015 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.discovery.util;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Comparator;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import com.netflix.appinfo.InstanceInfo;
import com.netflix.appinfo.InstanceInfo.ActionType;
import com.netflix.discovery.shared.Application;
import com.netflix.discovery.shared.Applications;
import com.netflix.discovery.util.EurekaEntityTransformers.Transformer;
/**
* Collection of functions operating on {@link Applications} and {@link Application} data
* structures. The functions are organized into groups with common prefix name:
* <ul>
* <li>select, take - queries over Eureka entity objects</li>
* <li>to - Eureka entity object transformers</li>
* <li>copy - copy Eureka entities, with aggregated {@link InstanceInfo} objects copied by reference</li>
* <li>deepCopy - copy Eureka entities, with aggregated {@link InstanceInfo} objects copied by value</li>
* <li>merge - merge two identical data structures</li>
* <li>count - statistical functions</li>
* <li>comparator - comparators for the domain objects</li>
* </ul>
*
* @author Tomasz Bak
*/
public final class EurekaEntityFunctions {
private EurekaEntityFunctions() {
}
public static Set<String> selectApplicationNames(Applications applications) {
Set<String> result = new HashSet<>();
for (Application app : applications.getRegisteredApplications()) {
result.add(app.getName());
}
return result;
}
public static Map<String, InstanceInfo> selectInstancesMappedById(Application application) {
Map<String, InstanceInfo> result = new HashMap<>();
for (InstanceInfo instance : application.getInstances()) {
result.put(instance.getId(), instance);
}
return result;
}
public static InstanceInfo selectInstance(Applications applications, String id) {
for (Application application : applications.getRegisteredApplications()) {
for (InstanceInfo instance : application.getInstances()) {
if (instance.getId().equals(id)) {
return instance;
}
}
}
return null;
}
public static InstanceInfo selectInstance(Applications applications, String appName, String id) {
Application application = applications.getRegisteredApplications(appName);
if (application != null) {
for (InstanceInfo instance : application.getInstances()) {
if (instance.getId().equals(id)) {
return instance;
}
}
}
return null;
}
public static InstanceInfo takeFirst(Applications applications) {
for (Application application : applications.getRegisteredApplications()) {
if (!application.getInstances().isEmpty()) {
return application.getInstances().get(0);
}
}
return null;
}
public static Collection<InstanceInfo> selectAll(Applications applications) {
List<InstanceInfo> all = new ArrayList<>();
for (Application a : applications.getRegisteredApplications()) {
all.addAll(a.getInstances());
}
return all;
}
public static Map<String, Application> toApplicationMap(List<InstanceInfo> instances) {
Map<String, Application> applicationMap = new HashMap<String, Application>();
for (InstanceInfo instance : instances) {
String appName = instance.getAppName();
Application application = applicationMap.get(appName);
if (application == null) {
applicationMap.put(appName, application = new Application(appName));
}
application.addInstance(instance);
}
return applicationMap;
}
public static Applications toApplications(Map<String, Application> applicationMap) {
Applications applications = new Applications();
for (Application application : applicationMap.values()) {
applications.addApplication(application);
}
return updateMeta(applications);
}
public static Applications toApplications(InstanceInfo... instances) {
return toApplications(Arrays.asList(instances));
}
public static Applications toApplications(List<InstanceInfo> instances) {
Applications result = new Applications();
for (InstanceInfo instance : instances) {
Application app = result.getRegisteredApplications(instance.getAppName());
if (app == null) {
app = new Application(instance.getAppName());
result.addApplication(app);
}
app.addInstance(instance);
}
return updateMeta(result);
}
public static Applications copyApplications(Applications source) {
Applications result = new Applications();
copyApplications(source, result);
return updateMeta(result);
}
public static void copyApplications(Applications source, Applications result) {
if (source != null) {
for (Application app : source.getRegisteredApplications()) {
result.addApplication(new Application(app.getName(), app.getInstances()));
}
}
}
public static Application copyApplication(Application application) {
Application copy = new Application(application.getName());
for (InstanceInfo instance : application.getInstances()) {
copy.addInstance(instance);
}
return copy;
}
public static void copyApplication(Application source, Application result) {
if (source != null) {
for (InstanceInfo instance : source.getInstances()) {
result.addInstance(instance);
}
}
}
public static void copyInstances(Collection<InstanceInfo> instances, Applications result) {
if (instances != null) {
for (InstanceInfo instance : instances) {
Application app = result.getRegisteredApplications(instance.getAppName());
if (app == null) {
app = new Application(instance.getAppName());
result.addApplication(app);
}
app.addInstance(instance);
}
}
}
public static Collection<InstanceInfo> copyInstances(Collection<InstanceInfo> instances, ActionType actionType) {
List<InstanceInfo> result = new ArrayList<>();
for (InstanceInfo instance : instances) {
result.add(copyInstance(instance, actionType));
}
return result;
}
public static InstanceInfo copyInstance(InstanceInfo original, ActionType actionType) {
InstanceInfo copy = new InstanceInfo(original);
copy.setActionType(actionType);
return copy;
}
public static void deepCopyApplication(Application source, Application result, Transformer<InstanceInfo> transformer) {
for (InstanceInfo instance : source.getInstances()) {
InstanceInfo copy = transformer.apply(instance);
if (copy == instance) {
copy = new InstanceInfo(instance);
}
result.addInstance(copy);
}
}
public static Application deepCopyApplication(Application source) {
Application result = new Application(source.getName());
deepCopyApplication(source, result, EurekaEntityTransformers.<InstanceInfo>identity());
return result;
}
public static Applications deepCopyApplications(Applications source) {
Applications result = new Applications();
for (Application application : source.getRegisteredApplications()) {
result.addApplication(deepCopyApplication(application));
}
return updateMeta(result);
}
public static Applications mergeApplications(Applications first, Applications second) {
Set<String> firstNames = selectApplicationNames(first);
Set<String> secondNames = selectApplicationNames(second);
Set<String> allNames = new HashSet<String>(firstNames);
allNames.addAll(secondNames);
Applications merged = new Applications();
for (String appName : allNames) {
if (firstNames.contains(appName)) {
if (secondNames.contains(appName)) {
merged.addApplication(mergeApplication(first.getRegisteredApplications(appName), second.getRegisteredApplications(appName)));
} else {
merged.addApplication(copyApplication(first.getRegisteredApplications(appName)));
}
} else {
merged.addApplication(copyApplication(second.getRegisteredApplications(appName)));
}
}
return updateMeta(merged);
}
public static Application mergeApplication(Application first, Application second) {
if (!first.getName().equals(second.getName())) {
throw new IllegalArgumentException("Cannot merge applications with different names");
}
Application merged = copyApplication(first);
for (InstanceInfo instance : second.getInstances()) {
switch (instance.getActionType()) {
case ADDED:
case MODIFIED:
merged.addInstance(instance);
break;
case DELETED:
merged.removeInstance(instance);
}
}
return merged;
}
public static Applications updateMeta(Applications applications) {
applications.setVersion(1L);
applications.setAppsHashCode(applications.getReconcileHashCode());
return applications;
}
public static int countInstances(Applications applications) {
int count = 0;
for (Application application : applications.getRegisteredApplications()) {
count += application.getInstances().size();
}
return count;
}
public static Comparator<InstanceInfo> comparatorByAppNameAndId() {
return INSTANCE_APP_ID_COMPARATOR;
}
private static class InstanceAppIdComparator implements Comparator<InstanceInfo> {
@Override
public int compare(InstanceInfo o1, InstanceInfo o2) {
int ac = compareStrings(o1.getAppName(), o2.getAppName());
if (ac != 0) {
return ac;
}
return compareStrings(o1.getId(), o2.getId());
}
private int compareStrings(String s1, String s2) {
if (s1 == null) {
if (s2 != null) {
return -1;
}
}
if (s2 == null) {
return 1;
}
return s1.compareTo(s2);
}
}
private static final Comparator<InstanceInfo> INSTANCE_APP_ID_COMPARATOR = new InstanceAppIdComparator();
}