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.context.properties.migrator;
018
019import java.io.IOException;
020import java.io.InputStream;
021
022import org.apache.commons.logging.Log;
023import org.apache.commons.logging.LogFactory;
024
025import org.springframework.boot.configurationmetadata.ConfigurationMetadataRepository;
026import org.springframework.boot.configurationmetadata.ConfigurationMetadataRepositoryJsonBuilder;
027import org.springframework.boot.context.event.ApplicationFailedEvent;
028import org.springframework.boot.context.event.ApplicationPreparedEvent;
029import org.springframework.boot.context.event.ApplicationReadyEvent;
030import org.springframework.boot.context.event.SpringApplicationEvent;
031import org.springframework.context.ApplicationListener;
032import org.springframework.core.env.ConfigurableEnvironment;
033import org.springframework.core.io.Resource;
034import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
035
036/**
037 * An {@link ApplicationListener} that inspects the {@link ConfigurableEnvironment
038 * environment} for configuration keys that need to be migrated. Automatically renames the
039 * keys that have a matching replacement and log a report of what was discovered.
040 *
041 * @author Stephane Nicoll
042 * @since 2.0.0
043 */
044public class PropertiesMigrationListener
045                implements ApplicationListener<SpringApplicationEvent> {
046
047        private static final Log logger = LogFactory
048                        .getLog(PropertiesMigrationListener.class);
049
050        private PropertiesMigrationReport report;
051
052        private boolean reported;
053
054        @Override
055        public void onApplicationEvent(SpringApplicationEvent event) {
056                if (event instanceof ApplicationPreparedEvent) {
057                        onApplicationPreparedEvent((ApplicationPreparedEvent) event);
058                }
059                if (event instanceof ApplicationReadyEvent
060                                || event instanceof ApplicationFailedEvent) {
061                        logLegacyPropertiesReport();
062                }
063        }
064
065        private void onApplicationPreparedEvent(ApplicationPreparedEvent event) {
066                ConfigurationMetadataRepository repository = loadRepository();
067                PropertiesMigrationReporter reporter = new PropertiesMigrationReporter(repository,
068                                event.getApplicationContext().getEnvironment());
069                this.report = reporter.getReport();
070        }
071
072        private ConfigurationMetadataRepository loadRepository() {
073                try {
074                        return loadRepository(ConfigurationMetadataRepositoryJsonBuilder.create());
075                }
076                catch (IOException ex) {
077                        throw new IllegalStateException("Failed to load metadata", ex);
078                }
079        }
080
081        private ConfigurationMetadataRepository loadRepository(
082                        ConfigurationMetadataRepositoryJsonBuilder builder) throws IOException {
083                Resource[] resources = new PathMatchingResourcePatternResolver()
084                                .getResources("classpath*:/META-INF/spring-configuration-metadata.json");
085                for (Resource resource : resources) {
086                        try (InputStream inputStream = resource.getInputStream()) {
087                                builder.withJsonResource(inputStream);
088                        }
089                }
090                return builder.build();
091        }
092
093        private void logLegacyPropertiesReport() {
094                if (this.report == null || this.reported) {
095                        return;
096                }
097                String warningReport = this.report.getWarningReport();
098                if (warningReport != null) {
099                        logger.warn(warningReport);
100                }
101                String errorReport = this.report.getErrorReport();
102                if (errorReport != null) {
103                        logger.error(errorReport);
104                }
105                this.reported = true;
106        }
107
108}