001/*
002 * Copyright 2012-2016 the original author or authors.
003 *
004 * Licensed under the Apache License, Version 2.0 (the "License");
005 * you may not use this file except in compliance with the License.
006 * You may obtain a copy of the License at
007 *
008 *      http://www.apache.org/licenses/LICENSE-2.0
009 *
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS,
012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 * See the License for the specific language governing permissions and
014 * limitations under the License.
015 */
016
017package org.springframework.boot.autoconfigure;
018
019import java.util.concurrent.CountDownLatch;
020import java.util.concurrent.atomic.AtomicBoolean;
021
022import javax.validation.Validation;
023
024import org.apache.catalina.mbeans.MBeanFactory;
025
026import org.springframework.boot.context.event.ApplicationEnvironmentPreparedEvent;
027import org.springframework.boot.context.event.ApplicationFailedEvent;
028import org.springframework.boot.context.event.ApplicationReadyEvent;
029import org.springframework.boot.context.event.SpringApplicationEvent;
030import org.springframework.boot.logging.LoggingApplicationListener;
031import org.springframework.context.ApplicationListener;
032import org.springframework.core.annotation.Order;
033import org.springframework.format.support.DefaultFormattingConversionService;
034import org.springframework.http.converter.json.Jackson2ObjectMapperBuilder;
035import org.springframework.http.converter.support.AllEncompassingFormHttpMessageConverter;
036
037/**
038 * {@link ApplicationListener} to trigger early initialization in a background thread of
039 * time consuming tasks.
040 *
041 * @author Phillip Webb
042 * @author Andy Wilkinson
043 * @since 1.3.0
044 */
045@Order(LoggingApplicationListener.DEFAULT_ORDER + 1)
046public class BackgroundPreinitializer
047                implements ApplicationListener<SpringApplicationEvent> {
048
049        private static final AtomicBoolean preinitializationStarted = new AtomicBoolean(
050                        false);
051
052        private static final CountDownLatch preinitializationComplete = new CountDownLatch(1);
053
054        @Override
055        public void onApplicationEvent(SpringApplicationEvent event) {
056                if (event instanceof ApplicationEnvironmentPreparedEvent) {
057                        if (preinitializationStarted.compareAndSet(false, true)) {
058                                performPreinitialization();
059                        }
060                }
061                if ((event instanceof ApplicationReadyEvent
062                                || event instanceof ApplicationFailedEvent)
063                                && preinitializationStarted.get()) {
064                        try {
065                                preinitializationComplete.await();
066                        }
067                        catch (InterruptedException ex) {
068                                Thread.currentThread().interrupt();
069                        }
070                }
071        }
072
073        private void performPreinitialization() {
074                try {
075                        Thread thread = new Thread(new Runnable() {
076
077                                @Override
078                                public void run() {
079                                        runSafely(new MessageConverterInitializer());
080                                        runSafely(new MBeanFactoryInitializer());
081                                        runSafely(new ValidationInitializer());
082                                        runSafely(new JacksonInitializer());
083                                        runSafely(new ConversionServiceInitializer());
084                                        preinitializationComplete.countDown();
085                                }
086
087                                public void runSafely(Runnable runnable) {
088                                        try {
089                                                runnable.run();
090                                        }
091                                        catch (Throwable ex) {
092                                                // Ignore
093                                        }
094                                }
095
096                        }, "background-preinit");
097                        thread.start();
098                }
099                catch (Exception ex) {
100                        // This will fail on GAE where creating threads is prohibited. We can safely
101                        // continue but startup will be slightly slower as the initialization will now
102                        // happen on the main thread.
103                        preinitializationComplete.countDown();
104                }
105        }
106
107        /**
108         * Early initializer for Spring MessageConverters.
109         */
110        private static class MessageConverterInitializer implements Runnable {
111
112                @Override
113                public void run() {
114                        new AllEncompassingFormHttpMessageConverter();
115                }
116
117        }
118
119        /**
120         * Early initializer to load Tomcat MBean XML.
121         */
122        private static class MBeanFactoryInitializer implements Runnable {
123
124                @Override
125                public void run() {
126                        new MBeanFactory();
127                }
128
129        }
130
131        /**
132         * Early initializer for javax.validation.
133         */
134        private static class ValidationInitializer implements Runnable {
135
136                @Override
137                public void run() {
138                        Validation.byDefaultProvider().configure();
139                }
140
141        }
142
143        /**
144         * Early initializer for Jackson.
145         */
146        private static class JacksonInitializer implements Runnable {
147
148                @Override
149                public void run() {
150                        Jackson2ObjectMapperBuilder.json().build();
151                }
152
153        }
154
155        /**
156         * Early initializer for Spring's ConversionService.
157         */
158        private static class ConversionServiceInitializer implements Runnable {
159
160                @Override
161                public void run() {
162                        new DefaultFormattingConversionService();
163                }
164
165        }
166
167}