/** * Copyright 2017 - 2021 ZTE Corporation. *

* 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.onap.holmes.common.dropwizard.ioc.bundle; import com.codahale.metrics.health.HealthCheck; import com.fasterxml.jackson.databind.ObjectMapper; import com.google.common.collect.Lists; import io.dropwizard.Configuration; import io.dropwizard.ConfiguredBundle; import io.dropwizard.lifecycle.ServerLifecycleListener; import io.dropwizard.servlets.tasks.Task; import io.dropwizard.setup.Bootstrap; import io.dropwizard.setup.Environment; import org.eclipse.jetty.util.component.LifeCycle; import org.glassfish.hk2.api.ServiceLocator; import org.glassfish.hk2.api.ServiceLocatorFactory; import org.glassfish.hk2.utilities.ServiceLocatorUtilities; import org.glassfish.hk2.utilities.binding.AbstractBinder; import org.glassfish.jersey.servlet.ServletProperties; import org.jvnet.hk2.annotations.Service; import org.onap.holmes.common.dropwizard.ioc.annotation.*; import org.onap.holmes.common.dropwizard.ioc.utils.ServiceBinder; import org.onap.holmes.common.dropwizard.ioc.utils.ServiceLocatorHolder; import org.reflections.Reflections; import org.reflections.scanners.SubTypesScanner; import org.reflections.scanners.TypeAnnotationsScanner; import org.reflections.util.ClasspathHelper; import org.reflections.util.ConfigurationBuilder; import org.reflections.util.FilterBuilder; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import javax.ws.rs.Path; import javax.ws.rs.ext.Provider; import java.lang.annotation.Annotation; import java.lang.reflect.Field; import java.util.Arrays; import java.util.List; import java.util.Set; import java.util.stream.Collectors; /** * complete the integration of hK2 container and dropwizard * * @author hu.rui */ public class AutoConfigBundle implements ConfiguredBundle { private static final Logger LOG = LoggerFactory.getLogger(AutoConfigBundle.class); private ServiceLocator locator; private Reflections reflections; private Set> services; private Bootstrap bootstrap; AutoConfigBundle(final String packageName) { this(Lists.newArrayList(packageName)); } AutoConfigBundle(List packageNames) { FilterBuilder filterBuilder = new FilterBuilder(); packageNames.stream().forEach(packageName -> filterBuilder.include(FilterBuilder.prefix(packageName))); ConfigurationBuilder reflectionCfg = new ConfigurationBuilder(); packageNames.stream().forEach(packageName -> reflectionCfg.addUrls(ClasspathHelper.forPackage(packageName))); reflectionCfg.filterInputsBy(filterBuilder).setScanners(new SubTypesScanner(), new TypeAnnotationsScanner()); reflections = new Reflections(reflectionCfg); locator = ServiceLocatorFactory.getInstance().create("dw-hk2"); ServiceLocatorHolder.setLocator(locator); } public static AutoConfigBundleBuider newBuilder() { return new AutoConfigBundleBuider(); } @Override public void initialize(final Bootstrap bootstrap) { this.bootstrap = bootstrap; registerPreLoadService(); LOG.debug("Intialzing auto config bundle."); } private void registerPreLoadService() { registerService(PreLoad.class); } @Override public void run(final T configuration, final Environment environment) { registerConfigurationProvider(configuration, environment); registerEnvironment(environment); registerObjectMapper(environment); environment.getApplicationContext().getServletContext() .setAttribute(ServletProperties.SERVICE_LOCATOR, locator); registerService(PreBaseService.class); registerService(BaseService.class); registerService(PostBaseService.class); this.registerService(PreServiceLoad.class); registerServices(); registerLifecycle(environment); registerServerLifecycleListeners(environment); registerJettyLifeCycleListener(environment); registerTasks(environment); registerHealthChecks(environment); registerProviders(environment); registerResources(environment); environment.lifecycle().manage(new ServiceLocatorManaged(locator)); } private void registerProviders(Environment environment) { reflections.getSubTypesOf(Provider.class).stream().filter(services::contains) .forEach(providerKlass -> { try { environment.jersey().register(locator.getService(providerKlass)); } catch (Exception e) { LOG.warn("", e); } LOG.info("Registering Dropwizard Provider, class name : {}", providerKlass.getName()); }); } private void registerTasks(Environment environment) { reflections.getSubTypesOf(Task.class).stream().filter(services::contains).forEach(taskKlass -> { try { environment.admin().addTask(locator.getService(taskKlass)); } catch (Exception e) { LOG.warn("", e); } LOG.info("Registering Dropwizard Task, class name : {}", taskKlass.getName()); }); } private void registerJettyLifeCycleListener(Environment environment) { reflections.getSubTypesOf(LifeCycle.Listener.class).stream().filter(services::contains) .forEach(lifecycleListenerKlass -> { try { environment.lifecycle() .addLifeCycleListener(locator.getService(lifecycleListenerKlass)); } catch (Exception e) { LOG.warn("", e); } LOG.info("Registering Dropwizard lifecycleListener, class name : {}", lifecycleListenerKlass.getName()); }); } private void registerServerLifecycleListeners(Environment environment) { reflections.getSubTypesOf(ServerLifecycleListener.class).stream().filter(services::contains) .forEach(serverLifecycleListenerKlass -> { try { environment.lifecycle() .addServerLifecycleListener(locator.getService(serverLifecycleListenerKlass)); } catch (Exception e) { LOG.warn("", e); } LOG.info("Registering Dropwizard serverLifecycleListener, class name : {}", serverLifecycleListenerKlass.getName()); }); } private void registerLifecycle(Environment environment) { reflections.getSubTypesOf(LifeCycle.class).stream().filter(services::contains) .forEach(lifeCycleKlass -> { try { environment.lifecycle().manage(locator.getService(lifeCycleKlass)); } catch (Exception e) { LOG.warn("", e); } LOG.info("Registering Dropwizard LifeCycle, class name : {}", lifeCycleKlass.getName()); }); } private void registerObjectMapper(Environment environment) { final ObjectMapper objectMapper = environment.getObjectMapper(); ServiceLocatorUtilities.bind(locator, new AbstractBinder() { @Override protected void configure() { bind(objectMapper).to(ObjectMapper.class); LOG.info("Registering Dropwizard objectMapper, class name : {}", objectMapper.getClass().getName()); } }); } private void registerEnvironment(final Environment environment) { ServiceLocatorUtilities.bind(locator, new AbstractBinder() { @Override protected void configure() { bind(environment).to(Environment.class); LOG.info("Registering Dropwizard environment, class name : {}", environment.getClass().getName()); } }); } private void registerConfigurationProvider(final T configuration, final Environment environment) { ServiceLocatorUtilities.bind(locator, new AbstractBinder() { @Override protected void configure() { bind(configuration); LOG.info("Registering Dropwizard Configuration class name:{}", configuration.getClass().getName()); bind((Configuration) configuration).to(Configuration.class); LOG.info("Registering Dropwizard Configuration class name:{}", Configuration.class.getName()); } }); registerSubConfigure(configuration, environment); } private void registerSubConfigure(final T configuration, final Environment environment) { final List subDeclaredFields = Arrays.asList(configuration.getClass().getDeclaredFields()); List parentDeclaredFields = Arrays.asList(Configuration.class.getDeclaredFields()); List filtersubDeclaredFields = subDeclaredFields.stream() .filter(subDeclaredField -> !subDeclaredField.getType().isPrimitive()) .filter(subDeclaredField -> !subDeclaredField.getType().equals(String.class)) .filter(subDeclaredField -> !parentDeclaredFields.contains(subDeclaredField)) .collect(Collectors.toList()); ServiceLocatorUtilities.bind(locator, new AbstractBinder() { @Override protected void configure() { filtersubDeclaredFields.forEach(subField -> { subField.setAccessible(true); try { Object subConfig = subField.get(configuration); if (subConfig != null) { bind(subConfig); LOG.info("Registering Dropwizard Sub Configuration class name {}", subConfig.getClass().getName()); } } catch (Exception e) { LOG.error("bind sub config:{} fail", subField); } }); } }); } private void registerServices() { services = this.reflections.getTypesAnnotatedWith(Service.class, true); if (!services.isEmpty()) { ServiceLocatorUtilities.bind(locator, new ServiceBinder(services)); services.forEach(s -> LOG.info("Registering Dropwizard service, class name : {}", s.getName())); recordTimeCost(services); } else { LOG.warn("Registering Dropwizard service is empty"); } } private void registerResources(final Environment environment) { reflections.getTypesAnnotatedWith(Path.class).stream().forEach(resourceClass -> { LOG.info("begin Registering Dropwizard resource, class name : {}", resourceClass.getName()); try { Object resourceObject = locator.getService(resourceClass); if (resourceObject != null) { environment.jersey().register(resourceObject); LOG.info("Registering Dropwizard resource, class name : {}", resourceClass.getName()); } else { LOG.warn(resourceClass.getName() + " not use Service annotation"); } } catch (Exception e) { LOG.error("", e); } }); } private void registerHealthChecks(final Environment env) { reflections.getSubTypesOf(HealthCheck.class).stream().filter(services::contains) .forEach(healthCheckKlass -> { try { env.healthChecks().register(healthCheckKlass.getName(), locator.getService(healthCheckKlass)); } catch (Exception e) { LOG.warn("", e); } LOG.info("Registering Dropwizard healthCheck, class name : {}", healthCheckKlass.getName()); }); } private void registerService(Class annotationClazz) { Set> services = this.reflections.getTypesAnnotatedWith(annotationClazz, true); if (!services.isEmpty()) { ServiceLocatorUtilities.bind(locator, new ServiceBinder(services)); services.forEach(s -> LOG.info("{} Registering service, class name : {}", annotationClazz.getName(), s.getName())); recordTimeCost(services); } else { LOG.warn("Registering {} service is empty", annotationClazz.getName()); } } private void recordTimeCost(Set> services) { services.stream().filter(serviceClazz -> (serviceClazz.getAnnotation(Lazy.class) == null)) .peek(serviceClazz -> LOG.info("active service, class name : {}", serviceClazz.getName())) .forEach(serviceClazz -> { try { long startTime = System.currentTimeMillis(); locator.getService(serviceClazz); LOG.info("active service, class name : {},cost time:{}", serviceClazz.getName(), (System.currentTimeMillis() - startTime)); } catch (Exception e) { LOG.warn("", e); } }); } }