fe9ee5a1452c9c982a1d341eca3b90fb6e91fb46
[holmes/common.git] / holmes-actions / src / main / java / org / onap / holmes / common / dropwizard / ioc / bundle / AutoConfigBundle.java
1 /**
2  * Copyright 2017 - 2021 ZTE Corporation.
3  * <p>
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  * <p>
8  * http://www.apache.org/licenses/LICENSE-2.0
9  * <p>
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 package org.onap.holmes.common.dropwizard.ioc.bundle;
17
18 import com.codahale.metrics.health.HealthCheck;
19 import com.fasterxml.jackson.databind.ObjectMapper;
20 import com.google.common.collect.Lists;
21 import io.dropwizard.Configuration;
22 import io.dropwizard.ConfiguredBundle;
23 import io.dropwizard.lifecycle.ServerLifecycleListener;
24 import io.dropwizard.servlets.tasks.Task;
25 import io.dropwizard.setup.Bootstrap;
26 import io.dropwizard.setup.Environment;
27 import org.eclipse.jetty.util.component.LifeCycle;
28 import org.glassfish.hk2.api.ServiceLocator;
29 import org.glassfish.hk2.api.ServiceLocatorFactory;
30 import org.glassfish.hk2.utilities.ServiceLocatorUtilities;
31 import org.glassfish.hk2.utilities.binding.AbstractBinder;
32 import org.glassfish.jersey.servlet.ServletProperties;
33 import org.jvnet.hk2.annotations.Service;
34 import org.onap.holmes.common.dropwizard.ioc.annotation.*;
35 import org.onap.holmes.common.dropwizard.ioc.utils.ServiceBinder;
36 import org.onap.holmes.common.dropwizard.ioc.utils.ServiceLocatorHolder;
37 import org.reflections.Reflections;
38 import org.reflections.scanners.SubTypesScanner;
39 import org.reflections.scanners.TypeAnnotationsScanner;
40 import org.reflections.util.ClasspathHelper;
41 import org.reflections.util.ConfigurationBuilder;
42 import org.reflections.util.FilterBuilder;
43 import org.slf4j.Logger;
44 import org.slf4j.LoggerFactory;
45
46 import javax.ws.rs.Path;
47 import javax.ws.rs.ext.Provider;
48 import java.lang.annotation.Annotation;
49 import java.lang.reflect.Field;
50 import java.util.Arrays;
51 import java.util.List;
52 import java.util.Set;
53 import java.util.stream.Collectors;
54
55 /**
56  * complete the integration of hK2 container and dropwizard
57  *
58  * @author hu.rui
59  */
60
61 public class AutoConfigBundle<T extends Configuration> implements ConfiguredBundle<T> {
62
63     private static final Logger LOG = LoggerFactory.getLogger(AutoConfigBundle.class);
64
65     private ServiceLocator locator;
66     private Reflections reflections;
67     private Set<Class<?>> services;
68
69     private Bootstrap<?> bootstrap;
70
71
72     AutoConfigBundle(final String packageName) {
73         this(Lists.newArrayList(packageName));
74     }
75
76     AutoConfigBundle(List<String> packageNames) {
77         FilterBuilder filterBuilder = new FilterBuilder();
78
79         packageNames.stream().forEach(packageName -> filterBuilder.include(FilterBuilder.prefix(packageName)));
80         ConfigurationBuilder reflectionCfg = new ConfigurationBuilder();
81
82         packageNames.stream().forEach(packageName -> reflectionCfg.addUrls(ClasspathHelper.forPackage(packageName)));
83
84         reflectionCfg.filterInputsBy(filterBuilder).setScanners(new SubTypesScanner(),
85                 new TypeAnnotationsScanner());
86         reflections = new Reflections(reflectionCfg);
87
88         locator = ServiceLocatorFactory.getInstance().create("dw-hk2");
89
90         ServiceLocatorHolder.setLocator(locator);
91
92     }
93
94     public static <T extends Configuration> AutoConfigBundleBuider<T> newBuilder() {
95         return new AutoConfigBundleBuider<T>();
96     }
97
98     @Override
99     public void initialize(final Bootstrap<?> bootstrap) {
100
101         this.bootstrap = bootstrap;
102         registerPreLoadService();
103
104         LOG.debug("Intialzing auto config bundle.");
105     }
106
107     private void registerPreLoadService() {
108         registerService(PreLoad.class);
109     }
110
111
112     @Override
113     public void run(final T configuration, final Environment environment) {
114
115         registerConfigurationProvider(configuration, environment);
116
117         registerEnvironment(environment);
118         registerObjectMapper(environment);
119
120         environment.getApplicationContext().getServletContext()
121                 .setAttribute(ServletProperties.SERVICE_LOCATOR, locator);
122
123         registerService(PreBaseService.class);
124         registerService(BaseService.class);
125         registerService(PostBaseService.class);
126         this.registerService(PreServiceLoad.class);
127
128
129         registerServices();
130
131         registerLifecycle(environment);
132         registerServerLifecycleListeners(environment);
133         registerJettyLifeCycleListener(environment);
134         registerTasks(environment);
135         registerHealthChecks(environment);
136         registerProviders(environment);
137         registerResources(environment);
138
139         environment.lifecycle().manage(new ServiceLocatorManaged(locator));
140
141     }
142
143
144     private void registerProviders(Environment environment) {
145         reflections.getSubTypesOf(Provider.class).stream().filter(services::contains)
146                 .forEach(providerKlass -> {
147                     try {
148                         environment.jersey().register(locator.getService(providerKlass));
149                     } catch (Exception e) {
150                         LOG.warn("", e);
151                     }
152
153                     LOG.info("Registering Dropwizard Provider, class name : {}", providerKlass.getName());
154
155                 });
156
157     }
158
159     private void registerTasks(Environment environment) {
160         reflections.getSubTypesOf(Task.class).stream().filter(services::contains).forEach(taskKlass -> {
161             try {
162                 environment.admin().addTask(locator.getService(taskKlass));
163             } catch (Exception e) {
164                 LOG.warn("", e);
165             }
166             LOG.info("Registering Dropwizard Task, class name : {}", taskKlass.getName());
167         });
168
169     }
170
171     private void registerJettyLifeCycleListener(Environment environment) {
172         reflections.getSubTypesOf(LifeCycle.Listener.class).stream().filter(services::contains)
173                 .forEach(lifecycleListenerKlass -> {
174                     try {
175                         environment.lifecycle()
176                                 .addLifeCycleListener(locator.getService(lifecycleListenerKlass));
177                     } catch (Exception e) {
178                         LOG.warn("", e);
179                     }
180                     LOG.info("Registering Dropwizard lifecycleListener, class name : {}",
181                             lifecycleListenerKlass.getName());
182                 });
183
184     }
185
186     private void registerServerLifecycleListeners(Environment environment) {
187
188         reflections.getSubTypesOf(ServerLifecycleListener.class).stream().filter(services::contains)
189                 .forEach(serverLifecycleListenerKlass -> {
190                     try {
191                         environment.lifecycle()
192                                 .addServerLifecycleListener(locator.getService(serverLifecycleListenerKlass));
193                     } catch (Exception e) {
194                         LOG.warn("", e);
195                     }
196                     LOG.info("Registering Dropwizard serverLifecycleListener, class name : {}",
197                             serverLifecycleListenerKlass.getName());
198                 });
199
200     }
201
202     private void registerLifecycle(Environment environment) {
203         reflections.getSubTypesOf(LifeCycle.class).stream().filter(services::contains)
204                 .forEach(lifeCycleKlass -> {
205                     try {
206                         environment.lifecycle().manage(locator.getService(lifeCycleKlass));
207                     } catch (Exception e) {
208                         LOG.warn("", e);
209                     }
210                     LOG.info("Registering Dropwizard LifeCycle, class name : {}", lifeCycleKlass.getName());
211                 });
212     }
213
214     private void registerObjectMapper(Environment environment) {
215
216         final ObjectMapper objectMapper = environment.getObjectMapper();
217
218         ServiceLocatorUtilities.bind(locator, new AbstractBinder() {
219             @Override
220             protected void configure() {
221                 bind(objectMapper).to(ObjectMapper.class);
222
223                 LOG.info("Registering Dropwizard objectMapper, class name : {}",
224                         objectMapper.getClass().getName());
225             }
226         });
227
228     }
229
230     private void registerEnvironment(final Environment environment) {
231
232         ServiceLocatorUtilities.bind(locator, new AbstractBinder() {
233             @Override
234             protected void configure() {
235                 bind(environment).to(Environment.class);
236
237                 LOG.info("Registering Dropwizard environment, class name : {}",
238                         environment.getClass().getName());
239             }
240         });
241
242     }
243
244     private void registerConfigurationProvider(final T configuration, final Environment environment) {
245
246         ServiceLocatorUtilities.bind(locator, new AbstractBinder() {
247             @Override
248             protected void configure() {
249                 bind(configuration);
250                 LOG.info("Registering Dropwizard Configuration class name:{}",
251                         configuration.getClass().getName());
252                 bind((Configuration) configuration).to(Configuration.class);
253                 LOG.info("Registering Dropwizard Configuration class name:{}",
254                         Configuration.class.getName());
255             }
256         });
257
258         registerSubConfigure(configuration, environment);
259
260     }
261
262     private void registerSubConfigure(final T configuration, final Environment environment) {
263         final List<Field> subDeclaredFields =
264                 Arrays.asList(configuration.getClass().getDeclaredFields());
265         List<Field> parentDeclaredFields = Arrays.asList(Configuration.class.getDeclaredFields());
266
267         List<Field> filtersubDeclaredFields = subDeclaredFields.stream()
268                 .filter(subDeclaredField -> !subDeclaredField.getType().isPrimitive())
269                 .filter(subDeclaredField -> !subDeclaredField.getType().equals(String.class))
270                 .filter(subDeclaredField -> !parentDeclaredFields.contains(subDeclaredField))
271                 .collect(Collectors.toList());
272
273         ServiceLocatorUtilities.bind(locator, new AbstractBinder() {
274             @Override
275             protected void configure() {
276                 filtersubDeclaredFields.forEach(subField -> {
277                     subField.setAccessible(true);
278                     try {
279                         Object subConfig = subField.get(configuration);
280                         if (subConfig != null) {
281                             bind(subConfig);
282                             LOG.info("Registering Dropwizard Sub Configuration class name {}",
283                                     subConfig.getClass().getName());
284                         }
285
286                     } catch (Exception e) {
287                         LOG.error("bind sub config:{} fail", subField);
288                     }
289                 });
290             }
291         });
292
293     }
294
295     private void registerServices() {
296         services = this.reflections.getTypesAnnotatedWith(Service.class, true);
297         if (!services.isEmpty()) {
298             ServiceLocatorUtilities.bind(locator, new ServiceBinder(services));
299             services.forEach(s -> LOG.info("Registering Dropwizard service, class name : {}", s.getName()));
300             recordTimeCost(services);
301         } else {
302             LOG.warn("Registering Dropwizard service is empty");
303         }
304
305     }
306
307
308     private void registerResources(final Environment environment) {
309         reflections.getTypesAnnotatedWith(Path.class).stream().forEach(resourceClass -> {
310
311             LOG.info("begin Registering Dropwizard resource, class name : {}", resourceClass.getName());
312             try {
313                 Object resourceObject = locator.getService(resourceClass);
314                 if (resourceObject != null) {
315                     environment.jersey().register(resourceObject);
316                     LOG.info("Registering Dropwizard resource, class name : {}", resourceClass.getName());
317                 } else {
318                     LOG.warn(resourceClass.getName() + " not use Service annotation");
319                 }
320             } catch (Exception e) {
321                 LOG.error("", e);
322             }
323         });
324     }
325
326     private void registerHealthChecks(final Environment env) {
327         reflections.getSubTypesOf(HealthCheck.class).stream().filter(services::contains)
328                 .forEach(healthCheckKlass -> {
329                     try {
330                         env.healthChecks().register(healthCheckKlass.getName(),
331                                 locator.getService(healthCheckKlass));
332                     } catch (Exception e) {
333                         LOG.warn("", e);
334                     }
335                     LOG.info("Registering Dropwizard healthCheck, class name : {}",
336                             healthCheckKlass.getName());
337                 });
338     }
339
340     private void registerService(Class<? extends Annotation> annotationClazz) {
341
342         Set<Class<?>> services = this.reflections.getTypesAnnotatedWith(annotationClazz, true);
343         if (!services.isEmpty()) {
344             ServiceLocatorUtilities.bind(locator, new ServiceBinder(services));
345             services.forEach(s -> LOG.info("{} Registering  service, class name : {}", annotationClazz.getName(),
346                     s.getName()));
347             recordTimeCost(services);
348         } else {
349             LOG.warn("Registering {} service is empty", annotationClazz.getName());
350         }
351     }
352
353     private void recordTimeCost(Set<Class<?>> services) {
354         services.stream().filter(serviceClazz -> (serviceClazz.getAnnotation(Lazy.class) == null))
355                 .peek(serviceClazz -> LOG.info("active service, class name : {}", serviceClazz.getName()))
356                 .forEach(serviceClazz -> {
357                     try {
358                         long startTime = System.currentTimeMillis();
359                         locator.getService(serviceClazz);
360                         LOG.info("active service, class name : {},cost time:{}", serviceClazz.getName(),
361                                 (System.currentTimeMillis() - startTime));
362                     } catch (Exception e) {
363                         LOG.warn("", e);
364                     }
365                 });
366     }
367 }