diff --git a/pom.xml b/pom.xml index 227ba871..9fe85de7 100644 --- a/pom.xml +++ b/pom.xml @@ -24,7 +24,7 @@ ${basedir} 4.0.27.Final 2.7.3 - 1.1.5.BUILD-SNAPSHOT + 1.2.0.BUILD-SNAPSHOT 1.2.2.BUILD-SNAPSHOT Brooklyn.BUILD-SNAPSHOT diff --git a/spring-cloud-netflix-eureka-client/src/main/java/org/springframework/cloud/netflix/eureka/CloudEurekaClient.java b/spring-cloud-netflix-eureka-client/src/main/java/org/springframework/cloud/netflix/eureka/CloudEurekaClient.java index 4338c1f9..3bb72451 100644 --- a/spring-cloud-netflix-eureka-client/src/main/java/org/springframework/cloud/netflix/eureka/CloudEurekaClient.java +++ b/spring-cloud-netflix-eureka-client/src/main/java/org/springframework/cloud/netflix/eureka/CloudEurekaClient.java @@ -16,16 +16,24 @@ package org.springframework.cloud.netflix.eureka; +import java.lang.reflect.Field; import java.util.concurrent.atomic.AtomicLong; +import java.util.concurrent.atomic.AtomicReference; -import lombok.extern.apachecommons.CommonsLog; - +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; import org.springframework.cloud.client.discovery.event.HeartbeatEvent; -import org.springframework.context.ApplicationContext; +import org.springframework.context.ApplicationEventPublisher; +import org.springframework.util.ReflectionUtils; import com.netflix.appinfo.ApplicationInfoManager; +import com.netflix.appinfo.InstanceInfo; +import com.netflix.appinfo.InstanceInfo.InstanceStatus; import com.netflix.discovery.DiscoveryClient; import com.netflix.discovery.EurekaClientConfig; +import com.netflix.discovery.shared.transport.EurekaHttpClient; + +import lombok.extern.apachecommons.CommonsLog; /** * Subclass of {@link DiscoveryClient} that sends a {@link HeartbeatEvent} when @@ -34,22 +42,55 @@ import com.netflix.discovery.EurekaClientConfig; */ @CommonsLog public class CloudEurekaClient extends DiscoveryClient { + private static final Log log = LogFactory.getLog(EurekaServiceRegistry.class); private final AtomicLong cacheRefreshedCount = new AtomicLong(0); - private ApplicationContext context; + private ApplicationEventPublisher publisher; + private Field eurekaTransportField; + private ApplicationInfoManager applicationInfoManager; + private AtomicReference eurekaHttpClient = new AtomicReference<>(); public CloudEurekaClient(ApplicationInfoManager applicationInfoManager, - EurekaClientConfig config, ApplicationContext context) { - this(applicationInfoManager, config, null, context); + EurekaClientConfig config, ApplicationEventPublisher publisher) { + this(applicationInfoManager, config, null, publisher); } public CloudEurekaClient(ApplicationInfoManager applicationInfoManager, EurekaClientConfig config, DiscoveryClientOptionalArgs args, - ApplicationContext context) { + ApplicationEventPublisher publisher) { super(applicationInfoManager, config, args); - this.context = context; + this.applicationInfoManager = applicationInfoManager; + this.publisher = publisher; + this.eurekaTransportField = ReflectionUtils.findField(DiscoveryClient.class, "eurekaTransport"); + ReflectionUtils.makeAccessible(this.eurekaTransportField); + } + + public ApplicationInfoManager getApplicationInfoManager() { + return applicationInfoManager; + } + + public void cancelOverrideStatus(InstanceInfo info) { + getEurekaHttpClient().deleteStatusOverride(info.getAppName(), info.getId(), info); + } + + EurekaHttpClient getEurekaHttpClient() { + if (this.eurekaHttpClient.get() == null) { + try { + Object eurekaTransport = this.eurekaTransportField.get(this); + Field registrationClientField = ReflectionUtils.findField(eurekaTransport.getClass(), "registrationClient"); + ReflectionUtils.makeAccessible(registrationClientField); + this.eurekaHttpClient.compareAndSet(null, (EurekaHttpClient) registrationClientField.get(eurekaTransport)); + } catch (IllegalAccessException e) { + log.error("error getting EurekaHttpClient", e); + } + } + return this.eurekaHttpClient.get(); + } + + public void setStatus(InstanceStatus newStatus, InstanceInfo info) { + getEurekaHttpClient().statusUpdate(info.getAppName(), info.getId(), newStatus, info); } @Override @@ -57,7 +98,7 @@ public class CloudEurekaClient extends DiscoveryClient { if (this.cacheRefreshedCount != null) { //might be called during construction and will be null long newCount = this.cacheRefreshedCount.incrementAndGet(); log.trace("onCacheRefreshed called with count: " + newCount); - this.context.publishEvent(new HeartbeatEvent(this, newCount)); + this.publisher.publishEvent(new HeartbeatEvent(this, newCount)); } } } diff --git a/spring-cloud-netflix-eureka-client/src/main/java/org/springframework/cloud/netflix/eureka/EurekaClientAutoConfiguration.java b/spring-cloud-netflix-eureka-client/src/main/java/org/springframework/cloud/netflix/eureka/EurekaClientAutoConfiguration.java index 46b0f1fd..230fbe1f 100644 --- a/spring-cloud-netflix-eureka-client/src/main/java/org/springframework/cloud/netflix/eureka/EurekaClientAutoConfiguration.java +++ b/spring-cloud-netflix-eureka-client/src/main/java/org/springframework/cloud/netflix/eureka/EurekaClientAutoConfiguration.java @@ -52,6 +52,7 @@ import org.springframework.util.StringUtils; import com.netflix.appinfo.ApplicationInfoManager; import com.netflix.appinfo.EurekaInstanceConfig; +import com.netflix.appinfo.HealthCheckHandler; import com.netflix.appinfo.InstanceInfo; import com.netflix.discovery.DiscoveryClient.DiscoveryClientOptionalArgs; import com.netflix.discovery.EurekaClient; @@ -76,16 +77,19 @@ import static org.springframework.cloud.commons.util.IdUtils.getDefaultInstanceI public class EurekaClientAutoConfiguration { @Value("${server.port:${SERVER_PORT:${PORT:8080}}}") - int nonSecurePort; + private int nonSecurePort; @Value("${management.port:${MANAGEMENT_PORT:${server.port:${SERVER_PORT:${PORT:8080}}}}}") - int managementPort; + private int managementPort; @Value("${eureka.instance.hostname:${EUREKA_INSTANCE_HOSTNAME:}}") - String hostname; + private String hostname; @Autowired - ConfigurableEnvironment env; + private ConfigurableEnvironment env; + + @Autowired(required = false) + private HealthCheckHandler healthCheckHandler; @Bean public HasFeatures eurekaFeature() { @@ -145,6 +149,20 @@ public class EurekaClientAutoConfiguration { return new EurekaDiscoveryClient(config, client); } + @Bean + public EurekaServiceRegistry eurekaServiceRegistry() { + return new EurekaServiceRegistry(); + } + + @Bean + public EurekaRegistration eurekaRegistration(EurekaClient eurekaClient, CloudEurekaInstanceConfig instanceConfig, ApplicationInfoManager applicationInfoManager) { + return EurekaRegistration.builder(instanceConfig) + .with(applicationInfoManager) + .with(eurekaClient) + .with(healthCheckHandler) + .build(); + } + @Bean @ConditionalOnMissingBean(value = DiscoveryClientOptionalArgs.class, search = SearchStrategy.CURRENT) public MutableDiscoveryClientOptionalArgs discoveryClientOptionalArgs() { diff --git a/spring-cloud-netflix-eureka-client/src/main/java/org/springframework/cloud/netflix/eureka/EurekaDiscoveryClientConfiguration.java b/spring-cloud-netflix-eureka-client/src/main/java/org/springframework/cloud/netflix/eureka/EurekaDiscoveryClientConfiguration.java index ff9c4d74..7a8e795e 100644 --- a/spring-cloud-netflix-eureka-client/src/main/java/org/springframework/cloud/netflix/eureka/EurekaDiscoveryClientConfiguration.java +++ b/spring-cloud-netflix-eureka-client/src/main/java/org/springframework/cloud/netflix/eureka/EurekaDiscoveryClientConfiguration.java @@ -22,9 +22,7 @@ import java.util.concurrent.atomic.AtomicInteger; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.actuate.endpoint.Endpoint; import org.springframework.boot.actuate.health.HealthAggregator; -import org.springframework.boot.actuate.health.HealthIndicator; import org.springframework.boot.actuate.health.OrderedHealthAggregator; -import org.springframework.boot.autoconfigure.condition.ConditionalOnBean; import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; @@ -40,10 +38,8 @@ import org.springframework.context.event.ContextClosedEvent; import org.springframework.context.event.EventListener; import org.springframework.core.Ordered; -import com.netflix.appinfo.ApplicationInfoManager; import com.netflix.appinfo.EurekaInstanceConfig; import com.netflix.appinfo.HealthCheckHandler; -import com.netflix.appinfo.InstanceInfo.InstanceStatus; import com.netflix.discovery.EurekaClient; import com.netflix.discovery.EurekaClientConfig; @@ -68,69 +64,36 @@ public class EurekaDiscoveryClientConfiguration implements SmartLifecycle, Order private AtomicInteger port = new AtomicInteger(0); - @Autowired - private CloudEurekaInstanceConfig instanceConfig; - - @Autowired(required = false) - private HealthCheckHandler healthCheckHandler; - @Autowired private ApplicationContext context; @Autowired - private ApplicationInfoManager applicationInfoManager; + private EurekaServiceRegistry serviceRegistry; @Autowired - private EurekaClient eurekaClient; + private EurekaRegistration registration; @Override public void start() { // only set the port if the nonSecurePort is 0 and this.port != 0 - if (this.port.get() != 0 && this.instanceConfig.getNonSecurePort() == 0) { - this.instanceConfig.setNonSecurePort(this.port.get()); + if (this.port.get() != 0 && this.registration.getNonSecurePort() == 0) { + this.registration.setNonSecurePort(this.port.get()); } // only initialize if nonSecurePort is greater than 0 and it isn't already running // because of containerPortInitializer below - if (!this.running.get() && this.instanceConfig.getNonSecurePort() > 0) { + if (!this.running.get() && this.registration.getNonSecurePort() > 0) { - maybeInitializeClient(); + this.serviceRegistry.register(this.registration); - if (log.isInfoEnabled()) { - log.info("Registering application " + this.instanceConfig.getAppname() - + " with eureka with status " - + this.instanceConfig.getInitialStatus()); - } - - this.applicationInfoManager - .setInstanceStatus(this.instanceConfig.getInitialStatus()); - - if (this.healthCheckHandler != null) { - this.eurekaClient.registerHealthCheck(this.healthCheckHandler); - } this.context.publishEvent( - new InstanceRegisteredEvent<>(this, this.instanceConfig)); + new InstanceRegisteredEvent<>(this, this.registration.getInstanceConfig())); this.running.set(true); } } - - private void maybeInitializeClient() { - // force initialization of possibly scoped proxies - this.applicationInfoManager.getInfo(); - this.eurekaClient.getApplications(); - } - @Override public void stop() { - if (this.applicationInfoManager.getInfo() != null) { - - if (log.isInfoEnabled()) { - log.info("Unregistering application " + this.instanceConfig.getAppname() - + " with eureka with status DOWN"); - } - - this.applicationInfoManager.setInstanceStatus(InstanceStatus.DOWN); - } + this.serviceRegistry.deregister(this.registration); this.running.set(false); } @@ -189,7 +152,6 @@ public class EurekaDiscoveryClientConfiguration implements SmartLifecycle, Order public void onApplicationEvent(ContextClosedEvent event) { // register in case meta data changed stop(); - this.eurekaClient.shutdown(); } @Configuration diff --git a/spring-cloud-netflix-eureka-client/src/main/java/org/springframework/cloud/netflix/eureka/EurekaRegistration.java b/spring-cloud-netflix-eureka-client/src/main/java/org/springframework/cloud/netflix/eureka/EurekaRegistration.java new file mode 100644 index 00000000..11a3f7d4 --- /dev/null +++ b/spring-cloud-netflix-eureka-client/src/main/java/org/springframework/cloud/netflix/eureka/EurekaRegistration.java @@ -0,0 +1,154 @@ +/* + * Copyright 2013-2016 the original author or authors. + * + * 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 org.springframework.cloud.netflix.eureka; + +import java.util.concurrent.atomic.AtomicReference; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.springframework.aop.framework.Advised; +import org.springframework.aop.support.AopUtils; +import org.springframework.cloud.client.serviceregistry.Registration; +import org.springframework.context.ApplicationEventPublisher; +import org.springframework.util.Assert; + +import com.netflix.appinfo.ApplicationInfoManager; +import com.netflix.appinfo.HealthCheckHandler; +import com.netflix.appinfo.InstanceInfo; +import com.netflix.discovery.EurekaClient; +import com.netflix.discovery.EurekaClientConfig; + +/** + * @author Spencer Gibb + */ +public class EurekaRegistration implements Registration { + private static final Log log = LogFactory.getLog(EurekaRegistration.class); + + private final EurekaClient eurekaClient; + private final AtomicReference cloudEurekaClient = new AtomicReference<>(); + private final CloudEurekaInstanceConfig instanceConfig; + private final ApplicationInfoManager applicationInfoManager; + private HealthCheckHandler healthCheckHandler; + + private EurekaRegistration(CloudEurekaInstanceConfig instanceConfig, EurekaClient eurekaClient, ApplicationInfoManager applicationInfoManager, HealthCheckHandler healthCheckHandler) { + this.eurekaClient = eurekaClient; + this.instanceConfig = instanceConfig; + this.applicationInfoManager = applicationInfoManager; + this.healthCheckHandler = healthCheckHandler; + } + + public static Builder builder(CloudEurekaInstanceConfig instanceConfig) { + return new Builder(instanceConfig); + } + + public static class Builder { + private final CloudEurekaInstanceConfig instanceConfig; + private ApplicationInfoManager applicationInfoManager; + private EurekaClient eurekaClient; + private HealthCheckHandler healthCheckHandler; + + private EurekaClientConfig clientConfig; + private ApplicationEventPublisher publisher; + + Builder(CloudEurekaInstanceConfig instanceConfig) { + this.instanceConfig = instanceConfig; + } + + public Builder with(ApplicationInfoManager applicationInfoManager) { + this.applicationInfoManager = applicationInfoManager; + return this; + } + + public Builder with(EurekaClient eurekaClient) { + this.eurekaClient = eurekaClient; + return this; + } + + public Builder with(HealthCheckHandler healthCheckHandler) { + this.healthCheckHandler = healthCheckHandler; + return this; + } + + public Builder with(EurekaClientConfig clientConfig, ApplicationEventPublisher publisher) { + this.clientConfig = clientConfig; + this.publisher = publisher; + return this; + } + + public EurekaRegistration build() { + Assert.notNull(instanceConfig, "instanceConfig may not be null"); + + if (this.applicationInfoManager == null) { + InstanceInfo instanceInfo = new InstanceInfoFactory().create(this.instanceConfig); + this.applicationInfoManager = new ApplicationInfoManager(this.instanceConfig, instanceInfo); + } + if (this.eurekaClient == null) { + Assert.notNull(this.clientConfig, "if eurekaClient is null, EurekaClientConfig may not be null"); + Assert.notNull(this.publisher, "if eurekaClient is null, ApplicationEventPublisher may not be null"); + + this.eurekaClient = new CloudEurekaClient(this.applicationInfoManager, this.clientConfig, this.publisher); + } + return new EurekaRegistration(instanceConfig, eurekaClient, applicationInfoManager, healthCheckHandler); + } + + } + + public CloudEurekaClient getEurekaClient() { + if (this.cloudEurekaClient.get() == null) { + try { + this.cloudEurekaClient.compareAndSet(null, getTargetObject(eurekaClient, CloudEurekaClient.class)); + } catch (Exception e) { + log.error("error getting CloudEurekaClient", e); + } + } + return this.cloudEurekaClient.get(); + } + + @SuppressWarnings({"unchecked"}) + protected T getTargetObject(Object proxy, Class targetClass) throws Exception { + if (AopUtils.isJdkDynamicProxy(proxy)) { + return (T) ((Advised) proxy).getTargetSource().getTarget(); + } else { + return (T) proxy; // expected to be cglib proxy then, which is simply a specialized class + } + } + + public CloudEurekaInstanceConfig getInstanceConfig() { + return instanceConfig; + } + + public ApplicationInfoManager getApplicationInfoManager() { + return applicationInfoManager; + } + + public HealthCheckHandler getHealthCheckHandler() { + return healthCheckHandler; + } + + public void setHealthCheckHandler(HealthCheckHandler healthCheckHandler) { + this.healthCheckHandler = healthCheckHandler; + } + + public void setNonSecurePort(int port) { + this.instanceConfig.setNonSecurePort(port); + } + + public int getNonSecurePort() { + return this.instanceConfig.getNonSecurePort(); + } +} diff --git a/spring-cloud-netflix-eureka-client/src/main/java/org/springframework/cloud/netflix/eureka/EurekaServiceRegistry.java b/spring-cloud-netflix-eureka-client/src/main/java/org/springframework/cloud/netflix/eureka/EurekaServiceRegistry.java new file mode 100644 index 00000000..8e72d655 --- /dev/null +++ b/spring-cloud-netflix-eureka-client/src/main/java/org/springframework/cloud/netflix/eureka/EurekaServiceRegistry.java @@ -0,0 +1,105 @@ +/* + * Copyright 2013-2016 the original author or authors. + * + * 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 org.springframework.cloud.netflix.eureka; + +import java.util.HashMap; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.springframework.cloud.client.serviceregistry.AutoServiceRegistration; +import org.springframework.cloud.client.serviceregistry.ServiceRegistry; + +import com.netflix.appinfo.InstanceInfo; + +/** + * @author Spencer Gibb + */ +public class EurekaServiceRegistry implements ServiceRegistry, AutoServiceRegistration { + + private static final Log log = LogFactory.getLog(EurekaServiceRegistry.class); + + @Override + public void register(EurekaRegistration reg) { + maybeInitializeClient(reg); + + if (log.isInfoEnabled()) { + log.info("Registering application " + reg.getInstanceConfig().getAppname() + + " with eureka with status " + + reg.getInstanceConfig().getInitialStatus()); + } + + reg.getApplicationInfoManager() + .setInstanceStatus(reg.getInstanceConfig().getInitialStatus()); + + if (reg.getHealthCheckHandler() != null) { + reg.getEurekaClient().registerHealthCheck(reg.getHealthCheckHandler()); + } + } + + private void maybeInitializeClient(EurekaRegistration reg) { + // force initialization of possibly scoped proxies + reg.getApplicationInfoManager().getInfo(); + reg.getEurekaClient().getApplications(); + } + + @Override + public void deregister(EurekaRegistration reg) { + if (reg.getApplicationInfoManager().getInfo() != null) { + + if (log.isInfoEnabled()) { + log.info("Unregistering application " + reg.getInstanceConfig().getAppname() + + " with eureka with status DOWN"); + } + + reg.getApplicationInfoManager().setInstanceStatus(InstanceInfo.InstanceStatus.DOWN); + + //TODO: on deregister or on context shutdown + reg.getEurekaClient().shutdown(); + } + } + + @Override + public void setStatus(EurekaRegistration registration, String status) { + InstanceInfo info = registration.getApplicationInfoManager().getInfo(); + + //TODO: howto deal with delete properly? + if ("RESET_OVERRIDE".equalsIgnoreCase(status)) { + registration.getEurekaClient().cancelOverrideStatus(info); + return; + } + + //TODO: howto deal with status types across discovery systems? + InstanceInfo.InstanceStatus newStatus = InstanceInfo.InstanceStatus.toEnum(status); + registration.getEurekaClient().setStatus(newStatus, info); + } + + @Override + public Object getStatus(EurekaRegistration registration) { + HashMap status = new HashMap<>(); + + InstanceInfo info = registration.getApplicationInfoManager().getInfo(); + status.put("status", info.getStatus().toString()); + status.put("overriddenStatus", info.getOverriddenStatus().toString()); + + return status; + } + + public void close() { + } + +} diff --git a/spring-cloud-netflix-eureka-client/src/test/java/org/springframework/cloud/netflix/eureka/sample/EurekaSampleApplication.java b/spring-cloud-netflix-eureka-client/src/test/java/org/springframework/cloud/netflix/eureka/sample/EurekaSampleApplication.java index fbf931fe..21567e2b 100644 --- a/spring-cloud-netflix-eureka-client/src/test/java/org/springframework/cloud/netflix/eureka/sample/EurekaSampleApplication.java +++ b/spring-cloud-netflix-eureka-client/src/test/java/org/springframework/cloud/netflix/eureka/sample/EurekaSampleApplication.java @@ -16,30 +16,59 @@ package org.springframework.cloud.netflix.eureka.sample; +import org.springframework.beans.BeansException; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.actuate.metrics.repository.InMemoryMetricRepository; import org.springframework.boot.autoconfigure.EnableAutoConfiguration; import org.springframework.boot.builder.SpringApplicationBuilder; import org.springframework.cloud.client.discovery.DiscoveryClient; +import org.springframework.cloud.client.discovery.event.InstanceRegisteredEvent; +import org.springframework.cloud.client.serviceregistry.ServiceRegistry; +import org.springframework.cloud.commons.util.InetUtils; import org.springframework.cloud.netflix.eureka.EnableEurekaClient; +import org.springframework.cloud.netflix.eureka.EurekaClientConfigBean; +import org.springframework.cloud.netflix.eureka.EurekaInstanceConfigBean; +import org.springframework.cloud.netflix.eureka.EurekaRegistration; +import org.springframework.context.ApplicationContext; +import org.springframework.context.ApplicationContextAware; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.Configuration; +import org.springframework.context.event.EventListener; import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.bind.annotation.RestController; import com.netflix.appinfo.HealthCheckHandler; import com.netflix.appinfo.InstanceInfo; +import java.io.Closeable; +import java.io.IOException; + +import static org.springframework.web.bind.annotation.RequestMethod.POST; + @Configuration @ComponentScan @EnableAutoConfiguration @RestController @EnableEurekaClient -public class EurekaSampleApplication { +public class EurekaSampleApplication implements ApplicationContextAware, Closeable { + + @Autowired + private DiscoveryClient discoveryClient; + + @Autowired + private ServiceRegistry serviceRegistry; + + @Autowired + private InetUtils inetUtils; @Autowired - DiscoveryClient discoveryClient; + private EurekaClientConfigBean clientConfig; + + private ApplicationContext context; + + private EurekaRegistration registration; @Bean public InMemoryMetricRepository inMemoryMetricRepository() { @@ -62,8 +91,43 @@ public class EurekaSampleApplication { return "Hello world "+discoveryClient.getLocalServiceInstance().getUri(); } + @Override + public void setApplicationContext(ApplicationContext context) throws BeansException { + this.context = context; + } + public static void main(String[] args) { new SpringApplicationBuilder(EurekaSampleApplication.class).web(true).run(args); } + @RequestMapping(path = "/register", method = POST) + public String register() { + EurekaInstanceConfigBean config = new EurekaInstanceConfigBean(inetUtils); + String appname = "customapp"; + config.setIpAddress("127.0.0.1"); + config.setHostname("localhost"); + config.setAppname(appname); + config.setVirtualHostName(appname); + config.setSecureVirtualHostName(appname); + config.setNonSecurePort(4444); + config.setInstanceId("127.0.0.1:customapp:4444"); + + this.registration = EurekaRegistration.builder(config) + .with(this.clientConfig, this.context) + .build(); + + this.serviceRegistry.register(this.registration); + return config.getInstanceId(); + } + + @RequestMapping(path = "/deregister", method = POST) + public String deregister() { + this.serviceRegistry.deregister(this.registration); + return "deregister"; + } + + @Override + public void close() throws IOException { + deregister(); + } }