001/* 002 * Copyright 2012-2017 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.env; 018 019import java.util.Random; 020import java.util.UUID; 021 022import org.apache.commons.logging.Log; 023import org.apache.commons.logging.LogFactory; 024 025import org.springframework.core.env.ConfigurableEnvironment; 026import org.springframework.core.env.PropertySource; 027import org.springframework.core.env.StandardEnvironment; 028import org.springframework.util.DigestUtils; 029import org.springframework.util.StringUtils; 030 031/** 032 * {@link PropertySource} that returns a random value for any property that starts with 033 * {@literal "random."}. Where the "unqualified property name" is the portion of the 034 * requested property name beyond the "random." prefix, this {@link PropertySource} 035 * returns: 036 * <ul> 037 * <li>When {@literal "int"}, a random {@link Integer} value, restricted by an optionally 038 * specified range.</li> 039 * <li>When {@literal "long"}, a random {@link Long} value, restricted by an optionally 040 * specified range.</li> 041 * <li>Otherwise, a {@code byte[]}.</li> 042 * </ul> 043 * The {@literal "random.int"} and {@literal "random.long"} properties supports a range 044 * suffix whose syntax is: 045 * <p> 046 * {@code OPEN value (,max) CLOSE} where the {@code OPEN,CLOSE} are any character and 047 * {@code value,max} are integers. If {@code max} is provided then {@code value} is the 048 * minimum value and {@code max} is the maximum (exclusive). 049 * 050 * @author Dave Syer 051 * @author Matt Benson 052 */ 053public class RandomValuePropertySource extends PropertySource<Random> { 054 055 /** 056 * Name of the random {@link PropertySource}. 057 */ 058 public static final String RANDOM_PROPERTY_SOURCE_NAME = "random"; 059 060 private static final String PREFIX = "random."; 061 062 private static final Log logger = LogFactory.getLog(RandomValuePropertySource.class); 063 064 public RandomValuePropertySource(String name) { 065 super(name, new Random()); 066 } 067 068 public RandomValuePropertySource() { 069 this(RANDOM_PROPERTY_SOURCE_NAME); 070 } 071 072 @Override 073 public Object getProperty(String name) { 074 if (!name.startsWith(PREFIX)) { 075 return null; 076 } 077 if (logger.isTraceEnabled()) { 078 logger.trace("Generating random property for '" + name + "'"); 079 } 080 return getRandomValue(name.substring(PREFIX.length())); 081 } 082 083 private Object getRandomValue(String type) { 084 if (type.equals("int")) { 085 return getSource().nextInt(); 086 } 087 if (type.equals("long")) { 088 return getSource().nextLong(); 089 } 090 String range = getRange(type, "int"); 091 if (range != null) { 092 return getNextIntInRange(range); 093 } 094 range = getRange(type, "long"); 095 if (range != null) { 096 return getNextLongInRange(range); 097 } 098 if (type.equals("uuid")) { 099 return UUID.randomUUID().toString(); 100 } 101 return getRandomBytes(); 102 } 103 104 private String getRange(String type, String prefix) { 105 if (type.startsWith(prefix)) { 106 int startIndex = prefix.length() + 1; 107 if (type.length() > startIndex) { 108 return type.substring(startIndex, type.length() - 1); 109 } 110 } 111 return null; 112 } 113 114 private int getNextIntInRange(String range) { 115 String[] tokens = StringUtils.commaDelimitedListToStringArray(range); 116 int start = Integer.parseInt(tokens[0]); 117 if (tokens.length == 1) { 118 return getSource().nextInt(start); 119 } 120 return start + getSource().nextInt(Integer.parseInt(tokens[1]) - start); 121 } 122 123 private long getNextLongInRange(String range) { 124 String[] tokens = StringUtils.commaDelimitedListToStringArray(range); 125 if (tokens.length == 1) { 126 return Math.abs(getSource().nextLong() % Long.parseLong(tokens[0])); 127 } 128 long lowerBound = Long.parseLong(tokens[0]); 129 long upperBound = Long.parseLong(tokens[1]) - lowerBound; 130 return lowerBound + Math.abs(getSource().nextLong() % upperBound); 131 } 132 133 private Object getRandomBytes() { 134 byte[] bytes = new byte[32]; 135 getSource().nextBytes(bytes); 136 return DigestUtils.md5DigestAsHex(bytes); 137 } 138 139 public static void addToEnvironment(ConfigurableEnvironment environment) { 140 environment.getPropertySources().addAfter( 141 StandardEnvironment.SYSTEM_ENVIRONMENT_PROPERTY_SOURCE_NAME, 142 new RandomValuePropertySource(RANDOM_PROPERTY_SOURCE_NAME)); 143 logger.trace("RandomValuePropertySource add to Environment"); 144 } 145 146}