001/* 002 * Copyright 2012-2018 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.nio.charset.StandardCharsets; 020import java.util.concurrent.CountDownLatch; 021import java.util.concurrent.atomic.AtomicBoolean; 022 023import javax.validation.Configuration; 024import javax.validation.Validation; 025 026import org.apache.catalina.mbeans.MBeanFactory; 027 028import org.springframework.boot.context.event.ApplicationFailedEvent; 029import org.springframework.boot.context.event.ApplicationReadyEvent; 030import org.springframework.boot.context.event.ApplicationStartingEvent; 031import org.springframework.boot.context.event.SpringApplicationEvent; 032import org.springframework.boot.context.logging.LoggingApplicationListener; 033import org.springframework.context.ApplicationListener; 034import org.springframework.core.annotation.Order; 035import org.springframework.format.support.DefaultFormattingConversionService; 036import org.springframework.http.converter.json.Jackson2ObjectMapperBuilder; 037import org.springframework.http.converter.support.AllEncompassingFormHttpMessageConverter; 038 039/** 040 * {@link ApplicationListener} to trigger early initialization in a background thread of 041 * time consuming tasks. 042 * <p> 043 * Set the {@link #IGNORE_BACKGROUNDPREINITIALIZER_PROPERTY_NAME} system property to 044 * {@code true} to disable this mechanism and let such initialization happen in the 045 * foreground. 046 * 047 * @author Phillip Webb 048 * @author Andy Wilkinson 049 * @author Artsiom Yudovin 050 * @since 1.3.0 051 */ 052@Order(LoggingApplicationListener.DEFAULT_ORDER + 1) 053public class BackgroundPreinitializer 054 implements ApplicationListener<SpringApplicationEvent> { 055 056 /** 057 * System property that instructs Spring Boot how to run pre initialization. When the 058 * property is set to {@code true}, no pre-initialization happens and each item is 059 * initialized in the foreground as it needs to. When the property is {@code false} 060 * (default), pre initialization runs in a separate thread in the background. 061 * @since 2.1.0 062 */ 063 public static final String IGNORE_BACKGROUNDPREINITIALIZER_PROPERTY_NAME = "spring.backgroundpreinitializer.ignore"; 064 065 private static final AtomicBoolean preinitializationStarted = new AtomicBoolean( 066 false); 067 068 private static final CountDownLatch preinitializationComplete = new CountDownLatch(1); 069 070 @Override 071 public void onApplicationEvent(SpringApplicationEvent event) { 072 if (!Boolean.getBoolean(IGNORE_BACKGROUNDPREINITIALIZER_PROPERTY_NAME) 073 && event instanceof ApplicationStartingEvent 074 && preinitializationStarted.compareAndSet(false, true)) { 075 performPreinitialization(); 076 } 077 if ((event instanceof ApplicationReadyEvent 078 || event instanceof ApplicationFailedEvent) 079 && preinitializationStarted.get()) { 080 try { 081 preinitializationComplete.await(); 082 } 083 catch (InterruptedException ex) { 084 Thread.currentThread().interrupt(); 085 } 086 } 087 } 088 089 private void performPreinitialization() { 090 try { 091 Thread thread = new Thread(new Runnable() { 092 093 @Override 094 public void run() { 095 runSafely(new ConversionServiceInitializer()); 096 runSafely(new ValidationInitializer()); 097 runSafely(new MessageConverterInitializer()); 098 runSafely(new MBeanFactoryInitializer()); 099 runSafely(new JacksonInitializer()); 100 runSafely(new CharsetInitializer()); 101 preinitializationComplete.countDown(); 102 } 103 104 public void runSafely(Runnable runnable) { 105 try { 106 runnable.run(); 107 } 108 catch (Throwable ex) { 109 // Ignore 110 } 111 } 112 113 }, "background-preinit"); 114 thread.start(); 115 } 116 catch (Exception ex) { 117 // This will fail on GAE where creating threads is prohibited. We can safely 118 // continue but startup will be slightly slower as the initialization will now 119 // happen on the main thread. 120 preinitializationComplete.countDown(); 121 } 122 } 123 124 /** 125 * Early initializer for Spring MessageConverters. 126 */ 127 private static class MessageConverterInitializer implements Runnable { 128 129 @Override 130 public void run() { 131 new AllEncompassingFormHttpMessageConverter(); 132 } 133 134 } 135 136 /** 137 * Early initializer to load Tomcat MBean XML. 138 */ 139 private static class MBeanFactoryInitializer implements Runnable { 140 141 @Override 142 public void run() { 143 new MBeanFactory(); 144 } 145 146 } 147 148 /** 149 * Early initializer for javax.validation. 150 */ 151 private static class ValidationInitializer implements Runnable { 152 153 @Override 154 public void run() { 155 Configuration<?> configuration = Validation.byDefaultProvider().configure(); 156 configuration.buildValidatorFactory().getValidator(); 157 } 158 159 } 160 161 /** 162 * Early initializer for Jackson. 163 */ 164 private static class JacksonInitializer implements Runnable { 165 166 @Override 167 public void run() { 168 Jackson2ObjectMapperBuilder.json().build(); 169 } 170 171 } 172 173 /** 174 * Early initializer for Spring's ConversionService. 175 */ 176 private static class ConversionServiceInitializer implements Runnable { 177 178 @Override 179 public void run() { 180 new DefaultFormattingConversionService(); 181 } 182 183 } 184 185 private static class CharsetInitializer implements Runnable { 186 187 @Override 188 public void run() { 189 StandardCharsets.UTF_8.name(); 190 } 191 192 } 193 194}