View Javadoc
1   package org.springframework.cloud.client.serviceregistry.endpoint;
2   
3   import static com.google.common.collect.Lists.newLinkedList;
4   import static java.lang.Character.isUpperCase;
5   
6   import com.google.common.collect.ImmutableMap;
7   
8   import lombok.extern.slf4j.Slf4j;
9   
10  import org.apache.commons.lang3.tuple.Pair;
11  import org.springframework.boot.actuate.endpoint.Endpoint;
12  import org.springframework.boot.actuate.endpoint.mvc.MvcEndpoint;
13  import org.springframework.cloud.client.serviceregistry.Registration;
14  import org.springframework.cloud.client.serviceregistry.ServiceRegistry;
15  import org.springframework.http.HttpStatus;
16  import org.springframework.http.ResponseEntity;
17  import org.springframework.jmx.export.annotation.ManagedAttribute;
18  import org.springframework.jmx.export.annotation.ManagedOperation;
19  import org.springframework.jmx.export.annotation.ManagedResource;
20  import org.springframework.util.Assert;
21  import org.springframework.web.bind.annotation.RequestBody;
22  import org.springframework.web.bind.annotation.RequestMapping;
23  import org.springframework.web.bind.annotation.RequestMethod;
24  import org.springframework.web.bind.annotation.ResponseBody;
25  
26  import java.util.Comparator;
27  import java.util.LinkedHashMap;
28  import java.util.List;
29  import java.util.Map;
30  import java.util.OptionalInt;
31  import java.util.stream.Collectors;
32  import java.util.stream.IntStream;
33  
34  /**
35   * Endpoint to display and set the service instance status using the service registry.
36   * see: {@link org.springframework.cloud.client.serviceregistry.endpoint.ServiceRegistryEndpoint}
37   *
38   * @author haolun zhang
39   */
40  @ManagedResource(description = "Can be used to display and set the service instance status using the service registry")
41  @SuppressWarnings("unchecked")
42  @Slf4j
43  public class MultiServiceRegistryEndpoint implements MvcEndpoint {
44  
45      private static final Comparator<ServiceRegistry> SERVICE_REGISTRY_COMPARATOR = (o1, o2) -> {
46          if (o1 != null && o2 != null) {
47              return o1.getClass().getSimpleName().compareTo(o2.getClass().getSimpleName());
48          } else if (o1 != null) {
49              return -1;
50          } else {
51              return 1;
52          }
53      };
54      private final Map<String, ServiceRegistry> serviceRegistries;
55      private List<Registration> registrations;
56      private List<Pair<String, Pair<Registration, ServiceRegistry>>> pairs = newLinkedList();
57  
58      public MultiServiceRegistryEndpoint(List<ServiceRegistry> serviceRegistries) {
59          if (serviceRegistries != null) {
60              this.serviceRegistries = serviceRegistries.stream()
61                  .sorted(SERVICE_REGISTRY_COMPARATOR) //
62                  .collect(Collectors.toMap(
63                      key -> discoveryServiceName(key.getClass()), value -> value, // key = name, value = serviceRegistry
64                      (oldValue, newValue) -> oldValue,                   // if same key, take the old key
65                      LinkedHashMap::new                                  // returns a LinkedHashMap, keep order
66                  ));
67          } else {
68              this.serviceRegistries = ImmutableMap.of();
69          }
70      }
71  
72      static String discoveryServiceName(final Class<?> registryOrRegistration) {
73          if (registryOrRegistration != null) {
74              final String simpleName = registryOrRegistration.getSimpleName();
75              final OptionalInt secondUpperCase = IntStream.range(1, simpleName.length())
76                  .filter(i -> isUpperCase(simpleName.charAt(i)))
77                  .findFirst();
78              return simpleName.substring(0, secondUpperCase.orElse(simpleName.length())).toLowerCase();
79          } else {
80              return null;
81          }
82      }
83  
84      public void setRegistrations(List<Registration> registrations) {
85          this.registrations = registrations;
86          if (registrations != null) {
87              registrations.forEach(registration -> {
88                  final String name = discoveryServiceName(registration.getClass());
89                  final ServiceRegistry<?> registry = this.serviceRegistries.get(name);
90                  if (registry != null) {
91                      this.pairs.add(Pair.of(name, Pair.of(registration, registry)));
92                  } else {
93                      log.warn("corresponding service registry for registration {} not found.", registration);
94                  }
95              });
96          }
97      }
98  
99      @RequestMapping(path = "instance-status", method = RequestMethod.POST)
100     @ResponseBody
101     @ManagedOperation
102     public ResponseEntity<?> setStatus(@RequestBody String status) {
103         Assert.notNull(status, "status may not by null");
104 
105         if (this.pairs.isEmpty()) {
106             return ResponseEntity.status(HttpStatus.NOT_FOUND).body("no registration found");
107         }
108 
109         this.pairs.forEach(pair -> {
110             final Registration registration = pair.getRight().getLeft();
111             final ServiceRegistry serviceRegistry = pair.getRight().getRight();
112             serviceRegistry.setStatus(registration, status);
113         });
114         return ResponseEntity.ok().build();
115     }
116 
117     @RequestMapping(path = "instance-status", method = RequestMethod.GET)
118     @ResponseBody
119     @ManagedAttribute
120     public ResponseEntity getStatus() {
121         if (this.pairs.isEmpty()) {
122             return ResponseEntity.status(HttpStatus.NOT_FOUND).body("no registration found");
123         }
124 
125         return ResponseEntity.ok().body(
126             pairs.stream() //
127                 .map(pair -> pair.getLeft() + ": " + pair.getRight().getRight().getStatus(pair.getRight().getLeft())) //
128                 .collect(Collectors.joining(","))
129         );
130     }
131 
132     @Override
133     public String getPath() {
134         return "/service-registry";
135     }
136 
137     @Override
138     public boolean isSensitive() {
139         return true;
140     }
141 
142     @Override
143     public Class<? extends Endpoint<?>> getEndpointType() {
144         return null;
145     }
146 }