Support image based banners
Add ImageBanner class that generates color ASCII art based on an image file (banner.gif, banner.jpg or banner.png). See gh-4647
This commit is contained in:
		
							parent
							
								
									6550bb4cf1
								
							
						
					
					
						commit
						58d77ec961
					
				| 
						 | 
					@ -0,0 +1,249 @@
 | 
				
			||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * Copyright 2012-2015 the original author or authors.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * Licensed under the Apache License, Version 2.0 (the "License");
 | 
				
			||||||
 | 
					 * you may not use this file except in compliance with the License.
 | 
				
			||||||
 | 
					 * You may obtain a copy of the License at
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 *      http://www.apache.org/licenses/LICENSE-2.0
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * Unless required by applicable law or agreed to in writing, software
 | 
				
			||||||
 | 
					 * distributed under the License is distributed on an "AS IS" BASIS,
 | 
				
			||||||
 | 
					 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | 
				
			||||||
 | 
					 * See the License for the specific language governing permissions and
 | 
				
			||||||
 | 
					 * limitations under the License.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					package org.springframework.boot;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import java.awt.Color;
 | 
				
			||||||
 | 
					import java.awt.Image;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import java.awt.image.BufferedImage;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import java.io.PrintStream;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import java.util.HashMap;
 | 
				
			||||||
 | 
					import java.util.Map;
 | 
				
			||||||
 | 
					import java.util.Map.Entry;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import javax.imageio.ImageIO;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import org.apache.commons.logging.Log;
 | 
				
			||||||
 | 
					import org.apache.commons.logging.LogFactory;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import org.springframework.boot.ansi.AnsiPropertySource;
 | 
				
			||||||
 | 
					import org.springframework.core.env.Environment;
 | 
				
			||||||
 | 
					import org.springframework.core.env.MutablePropertySources;
 | 
				
			||||||
 | 
					import org.springframework.core.env.PropertyResolver;
 | 
				
			||||||
 | 
					import org.springframework.core.env.PropertySourcesPropertyResolver;
 | 
				
			||||||
 | 
					import org.springframework.core.io.Resource;
 | 
				
			||||||
 | 
					import org.springframework.util.Assert;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * Banner implementation that prints ASCII art generated from an image resource
 | 
				
			||||||
 | 
					 * {@link Resource}.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * @author Craig Burke
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					public class ImageBanner implements Banner {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						private static final Log log = LogFactory.getLog(ImageBanner.class);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						private static final double RED_WEIGHT = 0.2126d;
 | 
				
			||||||
 | 
						private static final double GREEN_WEIGHT = 0.7152d;
 | 
				
			||||||
 | 
						private static final double BLUE_WEIGHT = 0.0722d;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						private static final int DEFAULT_MAX_WIDTH = 72;
 | 
				
			||||||
 | 
						private static final double DEFAULT_ASPECT_RATIO = 0.5d;
 | 
				
			||||||
 | 
						private static final boolean DEFAULT_DARK = false;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						private Resource image;
 | 
				
			||||||
 | 
						private Map<String, Color> colors = new HashMap<String, Color>();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						public ImageBanner(Resource image) {
 | 
				
			||||||
 | 
							Assert.notNull(image, "Image must not be null");
 | 
				
			||||||
 | 
							Assert.isTrue(image.exists(), "Image must exist");
 | 
				
			||||||
 | 
							this.image = image;
 | 
				
			||||||
 | 
							colorsInit();
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						private void colorsInit() {
 | 
				
			||||||
 | 
							this.colors.put("BLACK", new Color(0, 0, 0));
 | 
				
			||||||
 | 
							this.colors.put("RED", new Color(170, 0, 0));
 | 
				
			||||||
 | 
							this.colors.put("GREEN", new Color(0, 170, 0));
 | 
				
			||||||
 | 
							this.colors.put("YELLOW", new Color(170, 85, 0));
 | 
				
			||||||
 | 
							this.colors.put("BLUE", new Color(0, 0, 170));
 | 
				
			||||||
 | 
							this.colors.put("MAGENTA", new Color(170, 0, 170));
 | 
				
			||||||
 | 
							this.colors.put("CYAN", new Color(0, 170, 170));
 | 
				
			||||||
 | 
							this.colors.put("WHITE", new Color(170, 170, 170));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							this.colors.put("BRIGHT_BLACK", new Color(85, 85, 85));
 | 
				
			||||||
 | 
							this.colors.put("BRIGHT_RED", new Color(255, 85, 85));
 | 
				
			||||||
 | 
							this.colors.put("BRIGHT_GREEN", new Color(85, 255, 85));
 | 
				
			||||||
 | 
							this.colors.put("BRIGHT_YELLOW", new Color(255, 255, 85));
 | 
				
			||||||
 | 
							this.colors.put("BRIGHT_BLUE", new Color(85, 85, 255));
 | 
				
			||||||
 | 
							this.colors.put("BRIGHT_MAGENTA", new Color(255, 85, 255));
 | 
				
			||||||
 | 
							this.colors.put("BRIGHT_CYAN", new Color(85, 255, 255));
 | 
				
			||||||
 | 
							this.colors.put("BRIGHT_WHITE", new Color(255, 255, 255));
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						@Override
 | 
				
			||||||
 | 
						public void printBanner(Environment environment, Class<?> sourceClass, PrintStream out) {
 | 
				
			||||||
 | 
							String headlessProperty = System.getProperty("java.awt.headless");
 | 
				
			||||||
 | 
							try {
 | 
				
			||||||
 | 
								System.setProperty("java.awt.headless", "true");
 | 
				
			||||||
 | 
								BufferedImage sourceImage = ImageIO.read(this.image.getInputStream());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								int maxWidth = environment.getProperty("banner.image.max-width",
 | 
				
			||||||
 | 
										Integer.class, DEFAULT_MAX_WIDTH);
 | 
				
			||||||
 | 
								Double aspectRatio = environment.getProperty("banner.image.aspect-ratio",
 | 
				
			||||||
 | 
										Double.class, DEFAULT_ASPECT_RATIO);
 | 
				
			||||||
 | 
								boolean invert = environment.getProperty("banner.image.dark", Boolean.class,
 | 
				
			||||||
 | 
										DEFAULT_DARK);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								BufferedImage resizedImage = resizeImage(sourceImage, maxWidth, aspectRatio);
 | 
				
			||||||
 | 
								String banner = imageToBanner(resizedImage, invert);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								PropertyResolver ansiResolver = getAnsiResolver();
 | 
				
			||||||
 | 
								banner = ansiResolver.resolvePlaceholders(banner);
 | 
				
			||||||
 | 
								out.println(banner);
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							catch (Exception ex) {
 | 
				
			||||||
 | 
								log.warn("Image banner not printable: " + this.image + " (" + ex.getClass()
 | 
				
			||||||
 | 
										+ ": '" + ex.getMessage() + "')", ex);
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							finally {
 | 
				
			||||||
 | 
								System.setProperty("java.awt.headless", headlessProperty);
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						private PropertyResolver getAnsiResolver() {
 | 
				
			||||||
 | 
							MutablePropertySources sources = new MutablePropertySources();
 | 
				
			||||||
 | 
							sources.addFirst(new AnsiPropertySource("ansi", true));
 | 
				
			||||||
 | 
							return new PropertySourcesPropertyResolver(sources);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						private String imageToBanner(BufferedImage image, boolean dark) {
 | 
				
			||||||
 | 
							StringBuilder banner = new StringBuilder();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							for (int y = 0; y < image.getHeight(); y++) {
 | 
				
			||||||
 | 
								if (dark) {
 | 
				
			||||||
 | 
									banner.append("${AnsiBackground.BLACK}");
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								else {
 | 
				
			||||||
 | 
									banner.append("${AnsiBackground.DEFAULT}");
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								for (int x = 0; x < image.getWidth(); x++) {
 | 
				
			||||||
 | 
									Color color = new Color(image.getRGB(x, y), false);
 | 
				
			||||||
 | 
									banner.append(getFormatString(color, dark));
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								if (dark) {
 | 
				
			||||||
 | 
									banner.append("${AnsiBackground.DEFAULT}");
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								banner.append("${AnsiColor.DEFAULT}\n");
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							return banner.toString();
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						protected String getFormatString(Color color, boolean dark) {
 | 
				
			||||||
 | 
							String matchedColorName = null;
 | 
				
			||||||
 | 
							Double minColorDistance = null;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							for (Entry<String, Color> colorOption : this.colors.entrySet()) {
 | 
				
			||||||
 | 
								double distance = getColorDistance(color, colorOption.getValue());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								if (minColorDistance == null || distance < minColorDistance) {
 | 
				
			||||||
 | 
									minColorDistance = distance;
 | 
				
			||||||
 | 
									matchedColorName = colorOption.getKey();
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							return "${AnsiColor." + matchedColorName + "}" + getAsciiCharacter(color, dark);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						private static int getLuminance(Color color, boolean inverse) {
 | 
				
			||||||
 | 
							double red = color.getRed();
 | 
				
			||||||
 | 
							double green = color.getGreen();
 | 
				
			||||||
 | 
							double blue = color.getBlue();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							double luminance;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							if (inverse) {
 | 
				
			||||||
 | 
								luminance = (RED_WEIGHT * (255.0d - red)) + (GREEN_WEIGHT * (255.0d - green))
 | 
				
			||||||
 | 
										+ (BLUE_WEIGHT * (255.0d - blue));
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							else {
 | 
				
			||||||
 | 
								luminance = (RED_WEIGHT * red) + (GREEN_WEIGHT * green)
 | 
				
			||||||
 | 
										+ (BLUE_WEIGHT * blue);
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							return (int) Math.ceil((luminance / 255.0d) * 100);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						private static char getAsciiCharacter(Color color, boolean dark) {
 | 
				
			||||||
 | 
							double luminance = getLuminance(color, dark);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							if (luminance >= 90) {
 | 
				
			||||||
 | 
								return ' ';
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							else if (luminance >= 80) {
 | 
				
			||||||
 | 
								return '.';
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							else if (luminance >= 70) {
 | 
				
			||||||
 | 
								return '*';
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							else if (luminance >= 60) {
 | 
				
			||||||
 | 
								return ':';
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							else if (luminance >= 50) {
 | 
				
			||||||
 | 
								return 'o';
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							else if (luminance >= 40) {
 | 
				
			||||||
 | 
								return '&';
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							else if (luminance >= 30) {
 | 
				
			||||||
 | 
								return '8';
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							else if (luminance >= 20) {
 | 
				
			||||||
 | 
								return '#';
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							else {
 | 
				
			||||||
 | 
								return '@';
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						private static double getColorDistance(Color color1, Color color2) {
 | 
				
			||||||
 | 
							double redDelta = (color1.getRed() - color2.getRed()) * RED_WEIGHT;
 | 
				
			||||||
 | 
							double greenDelta = (color1.getGreen() - color2.getGreen()) * GREEN_WEIGHT;
 | 
				
			||||||
 | 
							double blueDelta = (color1.getBlue() - color2.getBlue()) * BLUE_WEIGHT;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							return Math.pow(redDelta, 2.0d) + Math.pow(greenDelta, 2.0d)
 | 
				
			||||||
 | 
									+ Math.pow(blueDelta, 2.0d);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						private static BufferedImage resizeImage(BufferedImage sourceImage, int maxWidth,
 | 
				
			||||||
 | 
								double aspectRatio) {
 | 
				
			||||||
 | 
							int width;
 | 
				
			||||||
 | 
							double resizeRatio;
 | 
				
			||||||
 | 
							if (sourceImage.getWidth() > maxWidth) {
 | 
				
			||||||
 | 
								resizeRatio = (double) maxWidth / (double) sourceImage.getWidth();
 | 
				
			||||||
 | 
								width = maxWidth;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							else {
 | 
				
			||||||
 | 
								resizeRatio = 1.0d;
 | 
				
			||||||
 | 
								width = sourceImage.getWidth();
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							int height = (int) (Math.ceil(resizeRatio * aspectRatio
 | 
				
			||||||
 | 
									* (double) sourceImage.getHeight()));
 | 
				
			||||||
 | 
							Image image = sourceImage.getScaledInstance(width, height, Image.SCALE_DEFAULT);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							BufferedImage resizedImage = new BufferedImage(image.getWidth(null),
 | 
				
			||||||
 | 
									image.getHeight(null), BufferedImage.TYPE_INT_RGB);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							resizedImage.getGraphics().drawImage(image, 0, 0, null);
 | 
				
			||||||
 | 
							return resizedImage;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -140,6 +140,7 @@ import org.springframework.web.context.support.StandardServletEnvironment;
 | 
				
			||||||
 * @author Christian Dupuis
 | 
					 * @author Christian Dupuis
 | 
				
			||||||
 * @author Stephane Nicoll
 | 
					 * @author Stephane Nicoll
 | 
				
			||||||
 * @author Jeremy Rickard
 | 
					 * @author Jeremy Rickard
 | 
				
			||||||
 | 
					 * @author Craig Burke
 | 
				
			||||||
 * @see #run(Object, String[])
 | 
					 * @see #run(Object, String[])
 | 
				
			||||||
 * @see #run(Object[], String[])
 | 
					 * @see #run(Object[], String[])
 | 
				
			||||||
 * @see #SpringApplication(Object...)
 | 
					 * @see #SpringApplication(Object...)
 | 
				
			||||||
| 
						 | 
					@ -569,9 +570,27 @@ public class SpringApplication {
 | 
				
			||||||
		if (this.banner != null) {
 | 
							if (this.banner != null) {
 | 
				
			||||||
			return this.banner;
 | 
								return this.banner;
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							Resource image = getBannerImage(environment, resourceLoader);
 | 
				
			||||||
 | 
							if (image.exists()) {
 | 
				
			||||||
 | 
								return new ImageBanner(image);
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		return DEFAULT_BANNER;
 | 
							return DEFAULT_BANNER;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						private Resource getBannerImage(Environment environment, ResourceLoader resourceLoader) {
 | 
				
			||||||
 | 
							String imageLocation = environment.getProperty("banner.image", "banner.gif");
 | 
				
			||||||
 | 
							Resource image = resourceLoader.getResource(imageLocation);
 | 
				
			||||||
 | 
							if (!image.exists()) {
 | 
				
			||||||
 | 
								image = resourceLoader.getResource("banner.jpg");
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							if (!image.exists()) {
 | 
				
			||||||
 | 
								image = resourceLoader.getResource("banner.png");
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							return image;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	private String createStringFromBanner(Banner banner, Environment environment)
 | 
						private String createStringFromBanner(Banner banner, Environment environment)
 | 
				
			||||||
			throws UnsupportedEncodingException {
 | 
								throws UnsupportedEncodingException {
 | 
				
			||||||
		ByteArrayOutputStream baos = new ByteArrayOutputStream();
 | 
							ByteArrayOutputStream baos = new ByteArrayOutputStream();
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,239 @@
 | 
				
			||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * Copyright 2012-2014 the original author or authors.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * Licensed under the Apache License, Version 2.0 (the "License");
 | 
				
			||||||
 | 
					 * you may not use this file except in compliance with the License.
 | 
				
			||||||
 | 
					 * You may obtain a copy of the License at
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 *      http://www.apache.org/licenses/LICENSE-2.0
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * Unless required by applicable law or agreed to in writing, software
 | 
				
			||||||
 | 
					 * distributed under the License is distributed on an "AS IS" BASIS,
 | 
				
			||||||
 | 
					 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | 
				
			||||||
 | 
					 * See the License for the specific language governing permissions and
 | 
				
			||||||
 | 
					 * limitations under the License.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					package org.springframework.boot;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import java.io.ByteArrayOutputStream;
 | 
				
			||||||
 | 
					import java.io.PrintStream;
 | 
				
			||||||
 | 
					import java.util.HashMap;
 | 
				
			||||||
 | 
					import java.util.Map;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import org.junit.Before;
 | 
				
			||||||
 | 
					import org.junit.Test;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import org.springframework.boot.ansi.AnsiBackground;
 | 
				
			||||||
 | 
					import org.springframework.boot.ansi.AnsiColor;
 | 
				
			||||||
 | 
					import org.springframework.boot.ansi.AnsiElement;
 | 
				
			||||||
 | 
					import org.springframework.boot.ansi.AnsiOutput;
 | 
				
			||||||
 | 
					import org.springframework.core.env.ConfigurableEnvironment;
 | 
				
			||||||
 | 
					import org.springframework.core.env.MapPropertySource;
 | 
				
			||||||
 | 
					import org.springframework.core.io.ClassPathResource;
 | 
				
			||||||
 | 
					import org.springframework.core.io.Resource;
 | 
				
			||||||
 | 
					import org.springframework.mock.env.MockEnvironment;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import static org.hamcrest.Matchers.containsString;
 | 
				
			||||||
 | 
					import static org.hamcrest.Matchers.equalTo;
 | 
				
			||||||
 | 
					import static org.hamcrest.Matchers.startsWith;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import static org.junit.Assert.assertThat;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * Tests for {@link ImageBanner}.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * @author Craig Burke
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					public class ImageBannerTests {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						private static final String IMAGE_BANNER_BLACK_AND_WHITE = "banners/black-and-white.gif";
 | 
				
			||||||
 | 
						private static final String IMAGE_BANNER_LARGE = "banners/large.gif";
 | 
				
			||||||
 | 
						private static final String IMAGE_BANNER_ALL_COLORS = "banners/colors.gif";
 | 
				
			||||||
 | 
						private static final String IMAGE_BANNER_GRADIENT = "banners/gradient.gif";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						private static final String BACKGROUND_DEFAULT_ANSI = getAnsiOutput(AnsiBackground.DEFAULT);
 | 
				
			||||||
 | 
						private static final String BACKGROUND_DARK_ANSI = getAnsiOutput(AnsiBackground.BLACK);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						private static final char HIGH_LUMINANCE_CHARACTER = ' ';
 | 
				
			||||||
 | 
						private static final char LOW_LUMINANCE_CHARACTER = '@';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						private static Map<String, Object> properties;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						@Before
 | 
				
			||||||
 | 
						public void setup() {
 | 
				
			||||||
 | 
							AnsiOutput.setEnabled(AnsiOutput.Enabled.ALWAYS);
 | 
				
			||||||
 | 
							properties = new HashMap<String, Object>();
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						@Test
 | 
				
			||||||
 | 
						public void renderDefaultBackground() {
 | 
				
			||||||
 | 
							String banner = printBanner(IMAGE_BANNER_BLACK_AND_WHITE);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							assertThat(banner, startsWith(BACKGROUND_DEFAULT_ANSI));
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						@Test
 | 
				
			||||||
 | 
						public void renderDarkBackground() {
 | 
				
			||||||
 | 
							setDark(true);
 | 
				
			||||||
 | 
							String banner = printBanner(IMAGE_BANNER_BLACK_AND_WHITE);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							assertThat(banner, startsWith(BACKGROUND_DARK_ANSI));
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						@Test
 | 
				
			||||||
 | 
						public void renderWhiteCharactersWithColors() {
 | 
				
			||||||
 | 
							String banner = printBanner(IMAGE_BANNER_BLACK_AND_WHITE);
 | 
				
			||||||
 | 
							String expectedFirstLine = getAnsiOutput(AnsiColor.BRIGHT_WHITE)
 | 
				
			||||||
 | 
									+ HIGH_LUMINANCE_CHARACTER;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							assertThat(banner, containsString(expectedFirstLine));
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						@Test
 | 
				
			||||||
 | 
						public void renderWhiteCharactersOnDarkBackground() {
 | 
				
			||||||
 | 
							setDark(true);
 | 
				
			||||||
 | 
							String banner = printBanner(IMAGE_BANNER_BLACK_AND_WHITE);
 | 
				
			||||||
 | 
							String expectedFirstLine = getAnsiOutput(AnsiColor.BRIGHT_WHITE)
 | 
				
			||||||
 | 
									+ LOW_LUMINANCE_CHARACTER;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							assertThat(banner, containsString(expectedFirstLine));
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						@Test
 | 
				
			||||||
 | 
						public void renderBlackCharactersOnDefaultBackground() {
 | 
				
			||||||
 | 
							String banner = printBanner(IMAGE_BANNER_BLACK_AND_WHITE);
 | 
				
			||||||
 | 
							String blackCharacter = getAnsiOutput(AnsiColor.BLACK) + LOW_LUMINANCE_CHARACTER;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							assertThat(banner, containsString(blackCharacter));
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						@Test
 | 
				
			||||||
 | 
						public void renderBlackCharactersOnDarkBackground() {
 | 
				
			||||||
 | 
							setDark(true);
 | 
				
			||||||
 | 
							String banner = printBanner(IMAGE_BANNER_BLACK_AND_WHITE);
 | 
				
			||||||
 | 
							String blackCharacter = getAnsiOutput(AnsiColor.BLACK) + HIGH_LUMINANCE_CHARACTER;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							assertThat(banner, containsString(blackCharacter));
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						@Test
 | 
				
			||||||
 | 
						public void renderBannerWithAllColors() {
 | 
				
			||||||
 | 
							String banner = printBanner(IMAGE_BANNER_ALL_COLORS);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							assertThat("Banner contains BLACK", banner,
 | 
				
			||||||
 | 
									containsString(getAnsiOutput(AnsiColor.BLACK)));
 | 
				
			||||||
 | 
							assertThat("Banner contains RED", banner,
 | 
				
			||||||
 | 
									containsString(getAnsiOutput(AnsiColor.RED)));
 | 
				
			||||||
 | 
							assertThat("Banner contains GREEN", banner,
 | 
				
			||||||
 | 
									containsString(getAnsiOutput(AnsiColor.GREEN)));
 | 
				
			||||||
 | 
							assertThat("Banner contains YELLOW", banner,
 | 
				
			||||||
 | 
									containsString(getAnsiOutput(AnsiColor.YELLOW)));
 | 
				
			||||||
 | 
							assertThat("Banner contains BLUE", banner,
 | 
				
			||||||
 | 
									containsString(getAnsiOutput(AnsiColor.BLUE)));
 | 
				
			||||||
 | 
							assertThat("Banner contains MAGENTA", banner,
 | 
				
			||||||
 | 
									containsString(getAnsiOutput(AnsiColor.MAGENTA)));
 | 
				
			||||||
 | 
							assertThat("Banner contains CYAN", banner,
 | 
				
			||||||
 | 
									containsString(getAnsiOutput(AnsiColor.CYAN)));
 | 
				
			||||||
 | 
							assertThat("Banner contains WHITE", banner,
 | 
				
			||||||
 | 
									containsString(getAnsiOutput(AnsiColor.WHITE)));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							assertThat("Banner contains BRIGHT_BLACK", banner,
 | 
				
			||||||
 | 
									containsString(getAnsiOutput(AnsiColor.BRIGHT_BLACK)));
 | 
				
			||||||
 | 
							assertThat("Banner contains BRIGHT_RED", banner,
 | 
				
			||||||
 | 
									containsString(getAnsiOutput(AnsiColor.BRIGHT_RED)));
 | 
				
			||||||
 | 
							assertThat("Banner contains BRIGHT_GREEN", banner,
 | 
				
			||||||
 | 
									containsString(getAnsiOutput(AnsiColor.BRIGHT_GREEN)));
 | 
				
			||||||
 | 
							assertThat("Banner contains BRIGHT_YELLOW", banner,
 | 
				
			||||||
 | 
									containsString(getAnsiOutput(AnsiColor.BRIGHT_YELLOW)));
 | 
				
			||||||
 | 
							assertThat("Banner contains BRIGHT_BLUE", banner,
 | 
				
			||||||
 | 
									containsString(getAnsiOutput(AnsiColor.BRIGHT_BLUE)));
 | 
				
			||||||
 | 
							assertThat("Banner contains BRIGHT_MAGENTA", banner,
 | 
				
			||||||
 | 
									containsString(getAnsiOutput(AnsiColor.BRIGHT_MAGENTA)));
 | 
				
			||||||
 | 
							assertThat("Banner contains BRIGHT_CYAN", banner,
 | 
				
			||||||
 | 
									containsString(getAnsiOutput(AnsiColor.BRIGHT_CYAN)));
 | 
				
			||||||
 | 
							assertThat("Banner contains BRIGHT_WHITE", banner,
 | 
				
			||||||
 | 
									containsString(getAnsiOutput(AnsiColor.BRIGHT_WHITE)));
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						@Test
 | 
				
			||||||
 | 
						public void renderSimpleGradient() {
 | 
				
			||||||
 | 
							AnsiOutput.setEnabled(AnsiOutput.Enabled.NEVER);
 | 
				
			||||||
 | 
							String banner = printBanner(IMAGE_BANNER_GRADIENT);
 | 
				
			||||||
 | 
							String expectedResult = "@#8&o:*. ";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							assertThat(banner, startsWith(expectedResult));
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						@Test
 | 
				
			||||||
 | 
						public void renderBannerWithDefaultAspectRatio() {
 | 
				
			||||||
 | 
							String banner = printBanner(IMAGE_BANNER_BLACK_AND_WHITE);
 | 
				
			||||||
 | 
							int bannerHeight = getBannerHeight(banner);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							assertThat(bannerHeight, equalTo(2));
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						@Test
 | 
				
			||||||
 | 
						public void renderBannerWithCustomAspectRatio() {
 | 
				
			||||||
 | 
							setAspectRatio(1.0d);
 | 
				
			||||||
 | 
							String banner = printBanner(IMAGE_BANNER_BLACK_AND_WHITE);
 | 
				
			||||||
 | 
							int bannerHeight = getBannerHeight(banner);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							assertThat(bannerHeight, equalTo(4));
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						@Test
 | 
				
			||||||
 | 
						public void renderLargeBanner() {
 | 
				
			||||||
 | 
							String banner = printBanner(IMAGE_BANNER_LARGE);
 | 
				
			||||||
 | 
							int bannerWidth = getBannerWidth(banner);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							assertThat(bannerWidth, equalTo(72));
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						@Test
 | 
				
			||||||
 | 
						public void renderLargeBannerWithACustomWidth() {
 | 
				
			||||||
 | 
							setMaxWidth(60);
 | 
				
			||||||
 | 
							String banner = printBanner(IMAGE_BANNER_LARGE);
 | 
				
			||||||
 | 
							int bannerWidth = getBannerWidth(banner);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							assertThat(bannerWidth, equalTo(60));
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						private int getBannerHeight(String banner) {
 | 
				
			||||||
 | 
							return banner.split("\n").length;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						private int getBannerWidth(String banner) {
 | 
				
			||||||
 | 
							String strippedBanner = banner.replaceAll("\u001B\\[.*?m", "");
 | 
				
			||||||
 | 
							String firstLine = strippedBanner.split("\n")[0];
 | 
				
			||||||
 | 
							return firstLine.length();
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						private static String getAnsiOutput(AnsiElement ansi) {
 | 
				
			||||||
 | 
							return "\u001B[" + ansi.toString() + "m";
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						private void setDark(boolean dark) {
 | 
				
			||||||
 | 
							properties.put("banner.image.dark", dark);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						private void setMaxWidth(int maxWidth) {
 | 
				
			||||||
 | 
							properties.put("banner.image.max-width", maxWidth);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						private void setAspectRatio(double aspectRatio) {
 | 
				
			||||||
 | 
							properties.put("banner.image.aspect-ratio", aspectRatio);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						private String printBanner(String imagePath) {
 | 
				
			||||||
 | 
							Resource image = new ClassPathResource(imagePath);
 | 
				
			||||||
 | 
							ImageBanner banner = new ImageBanner(image);
 | 
				
			||||||
 | 
							ConfigurableEnvironment environment = new MockEnvironment();
 | 
				
			||||||
 | 
							environment.getPropertySources().addLast(
 | 
				
			||||||
 | 
									new MapPropertySource("testConfig", properties));
 | 
				
			||||||
 | 
							ByteArrayOutputStream out = new ByteArrayOutputStream();
 | 
				
			||||||
 | 
							banner.printBanner(environment, getClass(), new PrintStream(out));
 | 
				
			||||||
 | 
							return out.toString();
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -19,8 +19,10 @@ package org.springframework.boot;
 | 
				
			||||||
import java.util.ArrayList;
 | 
					import java.util.ArrayList;
 | 
				
			||||||
import java.util.Arrays;
 | 
					import java.util.Arrays;
 | 
				
			||||||
import java.util.Collections;
 | 
					import java.util.Collections;
 | 
				
			||||||
 | 
					import java.util.HashMap;
 | 
				
			||||||
import java.util.LinkedHashSet;
 | 
					import java.util.LinkedHashSet;
 | 
				
			||||||
import java.util.List;
 | 
					import java.util.List;
 | 
				
			||||||
 | 
					import java.util.Map;
 | 
				
			||||||
import java.util.Set;
 | 
					import java.util.Set;
 | 
				
			||||||
import java.util.concurrent.atomic.AtomicReference;
 | 
					import java.util.concurrent.atomic.AtomicReference;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -66,6 +68,7 @@ import org.springframework.core.env.MapPropertySource;
 | 
				
			||||||
import org.springframework.core.env.PropertySource;
 | 
					import org.springframework.core.env.PropertySource;
 | 
				
			||||||
import org.springframework.core.env.StandardEnvironment;
 | 
					import org.springframework.core.env.StandardEnvironment;
 | 
				
			||||||
import org.springframework.core.io.DefaultResourceLoader;
 | 
					import org.springframework.core.io.DefaultResourceLoader;
 | 
				
			||||||
 | 
					import org.springframework.core.io.Resource;
 | 
				
			||||||
import org.springframework.core.io.ResourceLoader;
 | 
					import org.springframework.core.io.ResourceLoader;
 | 
				
			||||||
import org.springframework.test.context.support.TestPropertySourceUtils;
 | 
					import org.springframework.test.context.support.TestPropertySourceUtils;
 | 
				
			||||||
import org.springframework.util.StringUtils;
 | 
					import org.springframework.util.StringUtils;
 | 
				
			||||||
| 
						 | 
					@ -90,6 +93,7 @@ import static org.mockito.Mockito.verify;
 | 
				
			||||||
 * @author Christian Dupuis
 | 
					 * @author Christian Dupuis
 | 
				
			||||||
 * @author Stephane Nicoll
 | 
					 * @author Stephane Nicoll
 | 
				
			||||||
 * @author Jeremy Rickard
 | 
					 * @author Jeremy Rickard
 | 
				
			||||||
 | 
					 * @author Craig Burke
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
public class SpringApplicationTests {
 | 
					public class SpringApplicationTests {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -191,6 +195,32 @@ public class SpringApplicationTests {
 | 
				
			||||||
				.startsWith(String.format("Running a Test!%n%n123456"));
 | 
									.startsWith(String.format("Running a Test!%n%n123456"));
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						@Test
 | 
				
			||||||
 | 
						public void textBannerTakesPrecedence() throws Exception {
 | 
				
			||||||
 | 
							SpringApplication application = new SpringApplication(ExampleConfig.class);
 | 
				
			||||||
 | 
							BannerResourceLoaderStub resourceLoader = new BannerResourceLoaderStub();
 | 
				
			||||||
 | 
							resourceLoader.addResource("banner.gif", "banners/black-and-white.gif");
 | 
				
			||||||
 | 
							resourceLoader.addResource("banner.txt", "banners/foobar.txt");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							application.setWebEnvironment(false);
 | 
				
			||||||
 | 
							application.setResourceLoader(resourceLoader);
 | 
				
			||||||
 | 
							application.run();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							assertThat(this.output.toString()).startsWith("Foo Bar");
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						@Test
 | 
				
			||||||
 | 
						public void imageBannerLoads() throws Exception {
 | 
				
			||||||
 | 
							SpringApplication application = new SpringApplication(ExampleConfig.class);
 | 
				
			||||||
 | 
							BannerResourceLoaderStub resourceLoader = new BannerResourceLoaderStub();
 | 
				
			||||||
 | 
							resourceLoader.addResource("banner.gif", "banners/black-and-white.gif");
 | 
				
			||||||
 | 
							application.setWebEnvironment(false);
 | 
				
			||||||
 | 
							application.setResourceLoader(resourceLoader);
 | 
				
			||||||
 | 
							application.run();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							assertThat(this.output.toString()).startsWith("@");
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	@Test
 | 
						@Test
 | 
				
			||||||
	public void logsNoActiveProfiles() throws Exception {
 | 
						public void logsNoActiveProfiles() throws Exception {
 | 
				
			||||||
		SpringApplication application = new SpringApplication(ExampleConfig.class);
 | 
							SpringApplication application = new SpringApplication(ExampleConfig.class);
 | 
				
			||||||
| 
						 | 
					@ -1089,4 +1119,31 @@ public class SpringApplicationTests {
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						private static class BannerResourceLoaderStub extends DefaultResourceLoader {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							private Map<String, String> resources = new HashMap<String, String>();
 | 
				
			||||||
 | 
							Resource notFoundResource;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							BannerResourceLoaderStub() {
 | 
				
			||||||
 | 
								this.notFoundResource = super.getResource("classpath:foo/bar/foobar");
 | 
				
			||||||
 | 
								assert !this.notFoundResource.exists();
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							public void addResource(String file, String realPath) {
 | 
				
			||||||
 | 
								this.resources.put(file, realPath);
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							@Override
 | 
				
			||||||
 | 
							public Resource getResource(String s) {
 | 
				
			||||||
 | 
								if (this.resources.containsKey(s)) {
 | 
				
			||||||
 | 
									return super.getResource("classpath:" + this.resources.get(s));
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								else {
 | 
				
			||||||
 | 
									return this.notFoundResource;
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
										
											Binary file not shown.
										
									
								
							| 
		 After Width: | Height: | Size: 44 B  | 
										
											Binary file not shown.
										
									
								
							| 
		 After Width: | Height: | Size: 95 B  | 
| 
						 | 
					@ -0,0 +1 @@
 | 
				
			||||||
 | 
					Foo Bar
 | 
				
			||||||
										
											Binary file not shown.
										
									
								
							| 
		 After Width: | Height: | Size: 817 B  | 
										
											Binary file not shown.
										
									
								
							| 
		 After Width: | Height: | Size: 142 B  | 
		Loading…
	
		Reference in New Issue