org.springframework.cloud.netflix.eureka.server.EurekaServerInitializerConfiguration.java Source code

Java tutorial

Introduction

Here is the source code for org.springframework.cloud.netflix.eureka.server.EurekaServerInitializerConfiguration.java

Source

/*
 * Copyright 2013-2014 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.server;

import java.io.IOException;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;

import javax.servlet.ServletContext;
import javax.servlet.ServletContextEvent;

import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.aop.framework.ProxyFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.boot.logging.log4j.Log4JLoggingSystem;
import org.springframework.cloud.netflix.eureka.DataCenterAwareMarshallingStrategy;
import org.springframework.cloud.netflix.eureka.DiscoveryManagerInitializer;
import org.springframework.cloud.netflix.eureka.EurekaServerConfigBean;
import org.springframework.cloud.netflix.eureka.server.advice.LeaseManagerLite;
import org.springframework.cloud.netflix.eureka.server.advice.PiggybackMethodInterceptor;
import org.springframework.cloud.netflix.eureka.server.event.EurekaRegistryAvailableEvent;
import org.springframework.cloud.netflix.eureka.server.event.EurekaServerStartedEvent;
import org.springframework.cloud.netflix.eureka.server.event.LeaseManagerMessageBroker;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationListener;
import org.springframework.context.SmartLifecycle;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.Ordered;
import org.springframework.core.io.ClassPathResource;
import org.springframework.util.ReflectionUtils;
import org.springframework.web.context.ServletContextAware;

import com.netflix.blitz4j.LoggingConfiguration;
import com.netflix.discovery.converters.JsonXStream;
import com.netflix.discovery.converters.XmlXStream;
import com.netflix.eureka.EurekaBootStrap;
import com.netflix.eureka.EurekaServerConfig;
import com.netflix.eureka.EurekaServerConfigurationManager;
import com.netflix.eureka.InstanceRegistry;
import com.netflix.eureka.PeerAwareInstanceRegistry;

/**
 * @author Dave Syer
 *
 */
@Configuration
@EnableConfigurationProperties(EurekaServerConfigBean.class)
public class EurekaServerInitializerConfiguration implements ServletContextAware, SmartLifecycle, Ordered {

    private static Log logger = LogFactory.getLog(EurekaServerInitializerConfiguration.class);

    @Autowired
    private EurekaServerConfig eurekaServerConfig;

    private ServletContext servletContext;

    @Autowired
    private ApplicationContext applicationContext;

    private boolean running;

    private int order = 1;

    @Override
    public void setServletContext(ServletContext servletContext) {
        this.servletContext = servletContext;
    }

    @Bean
    @ConditionalOnMissingBean(DiscoveryManagerInitializer.class)
    public DiscoveryManagerInitializer discoveryManagerIntitializer() {
        return new DiscoveryManagerInitializer();
    }

    @Override
    public void start() {
        discoveryManagerIntitializer().init();
        new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    new EurekaBootStrap() {
                        @Override
                        protected void initEurekaEnvironment() {
                            try {
                                if (System.getProperty("log4j.configuration") == null) {
                                    System.setProperty("log4j.configuration",
                                            new ClassPathResource("log4j.properties", Log4JLoggingSystem.class)
                                                    .getURL().toString());
                                }
                            } catch (IOException e) {
                                // ignore
                            }
                            LoggingConfiguration.getInstance().configure();
                            EurekaServerConfigurationManager.getInstance().setConfiguration(eurekaServerConfig);
                            XmlXStream.getInstance().setMarshallingStrategy(
                                    new DataCenterAwareMarshallingStrategy(applicationContext));
                            JsonXStream.getInstance().setMarshallingStrategy(
                                    new DataCenterAwareMarshallingStrategy(applicationContext));
                            // PeerAwareInstanceRegistry.getInstance();
                            applicationContext.publishEvent(new EurekaRegistryAvailableEvent(eurekaServerConfig));
                        }
                    }.contextInitialized(new ServletContextEvent(servletContext));
                    logger.info("Started Eureka Server");
                    running = true;
                    applicationContext.publishEvent(new EurekaServerStartedEvent(eurekaServerConfig));
                } catch (Exception e) {
                    // Help!
                    logger.error("Could not initialize Eureka servlet context", e);
                }
            }
        }).start();
    }

    @Override
    public void stop() {
        running = false;
    }

    @Override
    public boolean isRunning() {
        return running;
    }

    @Override
    public int getPhase() {
        return 0;
    }

    @Override
    public boolean isAutoStartup() {
        return true;
    }

    @Override
    public void stop(Runnable callback) {
        callback.run();
    }

    @Override
    public int getOrder() {
        return order;
    }

    @Configuration
    @ConditionalOnClass(PeerAwareInstanceRegistry.class)
    protected static class RegistryInstanceProxyInitializer
            implements ApplicationListener<EurekaRegistryAvailableEvent> {

        @Autowired
        private ApplicationContext applicationContext;

        private PeerAwareInstanceRegistry instance;

        @Bean
        public LeaseManagerMessageBroker leaseManagerMessageBroker() {
            return new LeaseManagerMessageBroker();
        }

        @Override
        public void onApplicationEvent(EurekaRegistryAvailableEvent event) {
            if (instance == null) {
                instance = PeerAwareInstanceRegistry.getInstance();
                safeInit();
                replaceInstance(getProxyForInstance());
                expectRegistrations(1);
            }
        }

        private void safeInit() {
            Method method = ReflectionUtils.findMethod(InstanceRegistry.class, "postInit");
            ReflectionUtils.makeAccessible(method);
            ReflectionUtils.invokeMethod(method, instance);
        }

        private void replaceInstance(Object proxy) {
            Field field = ReflectionUtils.findField(PeerAwareInstanceRegistry.class, "instance");
            try {
                // Awful ugly hack to work around lack of DI in eureka
                field.setAccessible(true);
                Field modifiersField = Field.class.getDeclaredField("modifiers");
                modifiersField.setAccessible(true);
                modifiersField.setInt(field, field.getModifiers() & ~Modifier.FINAL);
                ReflectionUtils.setField(field, null, proxy);
            } catch (Exception e) {
                throw new IllegalStateException("Cannot modify instance registry", e);
            }
        }

        private Object getProxyForInstance() {
            // Wrap the instance registry...
            ProxyFactory factory = new ProxyFactory(instance);
            // ...with the LeaseManagerMessageBroker
            factory.addAdvice(new PiggybackMethodInterceptor(leaseManagerMessageBroker(), LeaseManagerLite.class));
            factory.addAdvice(new TrafficOpener());
            factory.setProxyTargetClass(true);

            return factory.getProxy();
        }

        private void expectRegistrations(int count) {
            /*
             * Setting expectedNumberOfRenewsPerMin to non-zero to ensure that even an
             * isolated server can adjust its eviction policy to the number of
             * registrations (when it's zero, even a successful registration won't reset
             * the rate threshold in InstanceRegistry.register()).
             */
            Field field = ReflectionUtils.findField(PeerAwareInstanceRegistry.class,
                    "expectedNumberOfRenewsPerMin");
            try {
                // Awful ugly hack to work around lack of DI in eureka
                field.setAccessible(true);
                int value = (int) ReflectionUtils.getField(field, instance);
                if (value == 0 && count > 0) {
                    ReflectionUtils.setField(field, instance, count);
                }
            } catch (Exception e) {
                throw new IllegalStateException("Cannot modify instance registry expected renews", e);
            }
        }

        /**
         * Additional aspect for intercepting method invocations on
         * PeerAwareInstanceRegistry. If
         * {@link PeerAwareInstanceRegistry#openForTraffic(int)} is called with a zero
         * argument, it means that leases are not automatically cancelled if the instance
         * hasn't sent any renewals recently. This happens for a standalone server. It
         * seems like a bad default, so we set it to the smallest non-zero value we can,
         * so that any instances that subsequently register can bump up the threshold.
         * 
         * @author Dave Syer
         *
         */
        private class TrafficOpener implements MethodInterceptor {

            @Override
            public Object invoke(MethodInvocation invocation) throws Throwable {
                if ("openForTraffic".equals(invocation.getMethod().getName())) {
                    int count = (int) invocation.getArguments()[0];
                    ReflectionUtils.invokeMethod(invocation.getMethod(), invocation.getThis(),
                            count == 0 ? 1 : count);
                    return null;
                }
                return invocation.proceed();
            }
        }

    }

}