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}