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.devtools.env; 018 019import java.util.Collections; 020import java.util.HashMap; 021import java.util.Map; 022 023import org.apache.commons.logging.Log; 024 025import org.springframework.boot.SpringApplication; 026import org.springframework.boot.devtools.logger.DevToolsLogFactory; 027import org.springframework.boot.devtools.restart.Restarter; 028import org.springframework.boot.env.EnvironmentPostProcessor; 029import org.springframework.core.Ordered; 030import org.springframework.core.annotation.Order; 031import org.springframework.core.env.ConfigurableEnvironment; 032import org.springframework.core.env.Environment; 033import org.springframework.core.env.MapPropertySource; 034import org.springframework.util.ClassUtils; 035 036/** 037 * {@link EnvironmentPostProcessor} to add properties that make sense when working at 038 * development time. 039 * 040 * @author Phillip Webb 041 * @author Andy Wilkinson 042 * @author Madhura Bhave 043 * @since 1.3.0 044 */ 045@Order(Ordered.LOWEST_PRECEDENCE) 046public class DevToolsPropertyDefaultsPostProcessor implements EnvironmentPostProcessor { 047 048 private static final Log logger = DevToolsLogFactory 049 .getLog(DevToolsPropertyDefaultsPostProcessor.class); 050 051 private static final String ENABLED = "spring.devtools.add-properties"; 052 053 private static final String WEB_LOGGING = "logging.level.web"; 054 055 private static final String[] WEB_ENVIRONMENT_CLASSES = { 056 "org.springframework.web.context.ConfigurableWebEnvironment", 057 "org.springframework.boot.web.reactive.context.ConfigurableReactiveWebEnvironment" }; 058 059 private static final Map<String, Object> PROPERTIES; 060 061 static { 062 Map<String, Object> properties = new HashMap<>(); 063 properties.put("spring.thymeleaf.cache", "false"); 064 properties.put("spring.freemarker.cache", "false"); 065 properties.put("spring.groovy.template.cache", "false"); 066 properties.put("spring.mustache.cache", "false"); 067 properties.put("server.servlet.session.persistent", "true"); 068 properties.put("spring.h2.console.enabled", "true"); 069 properties.put("spring.resources.cache.period", "0"); 070 properties.put("spring.resources.chain.cache", "false"); 071 properties.put("spring.template.provider.cache", "false"); 072 properties.put("spring.mvc.log-resolved-exception", "true"); 073 properties.put("server.error.include-stacktrace", "ALWAYS"); 074 properties.put("server.servlet.jsp.init-parameters.development", "true"); 075 properties.put("spring.reactor.stacktrace-mode.enabled", "true"); 076 PROPERTIES = Collections.unmodifiableMap(properties); 077 } 078 079 @Override 080 public void postProcessEnvironment(ConfigurableEnvironment environment, 081 SpringApplication application) { 082 if (isLocalApplication(environment)) { 083 if (canAddProperties(environment)) { 084 logger.info("Devtools property defaults active! Set '" + ENABLED 085 + "' to 'false' to disable"); 086 environment.getPropertySources() 087 .addLast(new MapPropertySource("devtools", PROPERTIES)); 088 } 089 if (isWebApplication(environment) 090 && !environment.containsProperty(WEB_LOGGING)) { 091 logger.info("For additional web related logging consider " 092 + "setting the '" + WEB_LOGGING + "' property to 'DEBUG'"); 093 } 094 } 095 } 096 097 private boolean isLocalApplication(ConfigurableEnvironment environment) { 098 return environment.getPropertySources().get("remoteUrl") == null; 099 } 100 101 private boolean canAddProperties(Environment environment) { 102 if (environment.getProperty(ENABLED, Boolean.class, true)) { 103 return isRestarterInitialized() || isRemoteRestartEnabled(environment); 104 } 105 return false; 106 } 107 108 private boolean isRestarterInitialized() { 109 try { 110 Restarter restarter = Restarter.getInstance(); 111 return (restarter != null && restarter.getInitialUrls() != null); 112 } 113 catch (Exception ex) { 114 return false; 115 } 116 } 117 118 private boolean isRemoteRestartEnabled(Environment environment) { 119 return environment.containsProperty("spring.devtools.remote.secret"); 120 } 121 122 private boolean isWebApplication(Environment environment) { 123 for (String candidate : WEB_ENVIRONMENT_CLASSES) { 124 Class<?> environmentClass = resolveClassName(candidate, 125 environment.getClass().getClassLoader()); 126 if (environmentClass != null && environmentClass.isInstance(environment)) { 127 return true; 128 } 129 } 130 return false; 131 } 132 133 private Class<?> resolveClassName(String candidate, ClassLoader classLoader) { 134 try { 135 return ClassUtils.resolveClassName(candidate, classLoader); 136 } 137 catch (IllegalArgumentException ex) { 138 return null; 139 } 140 } 141 142}