1 /* 2 * Copyright 2012-2013 smartics, Kronseder & Reiner GmbH 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 package de.smartics.properties.tutorial.property.constraints; 17 18 import static org.hamcrest.MatcherAssert.assertThat; 19 import static org.hamcrest.Matchers.allOf; 20 import static org.hamcrest.Matchers.containsString; 21 22 import java.util.Locale; 23 24 import org.junit.Before; 25 import org.junit.Test; 26 27 import de.smartics.exceptions.i18n.message.Messages; 28 import de.smartics.projectdoc.annotations.DocCategory; 29 import de.smartics.projectdoc.annotations.Document; 30 import de.smartics.projectdoc.annotations.topic.DocChapter; 31 import de.smartics.properties.api.config.domain.ConfigurationValidationException; 32 import de.smartics.properties.impl.config.classpath.ClasspathConfigurationProperties; 33 import de.smartics.properties.impl.config.classpath.ClasspathConfigurationPropertiesFactory; 34 35 /** 36 * This tutorial shows some of the validation messages. 37 */ 38 @Document(title = "Property Constraints", sortKey = "basics0055") 39 @DocCategory({ "basics" }) 40 // @DocTopic(path="basics", step="55") 41 public class ViolationMessagesTutorial 42 { 43 private ClasspathConfigurationProperties config; // NOPMD 44 45 private ConstraintsProperties properties; 46 47 @Before 48 public void setUp() 49 { 50 config = createConfiguration(); 51 properties = config.getProperties(ConstraintsProperties.class); 52 } 53 54 private static ClasspathConfigurationProperties createConfiguration() 55 { 56 final ClasspathConfigurationPropertiesFactory factory = 57 new ClasspathConfigurationPropertiesFactory(); 58 final ClasspathConfigurationProperties config = factory.create(); 59 config.addClassPathProperties(ConstraintsProperties.class); 60 return config; 61 } 62 63 /** 64 * <p> 65 * First let us examine the validation message if we set a property to an 66 * invalid value. 67 * </p> 68 * <p> 69 * The property is declared as follows: 70 * </p> 71 * 72 * <pre> 73 * @PropertyIntValueRange(value = { 0, 1, 2, 3, 5, 8, 13, 21, 50, 100 }, 74 * defaultValue = 8) 75 * @NotNull 76 * @PropertyLifecycle(access = AccessType.READ_WRITE) 77 * Integer storyPoint(); 78 * </pre> 79 * <p> 80 * The property is set to an invalid value: 81 * </p> 82 * {@insertCode} 83 * <p> 84 * The resulting message is: 85 * 86 * <pre> 87 * TITLE=Configuration Problem 88 * SUMMARY=Configuration ':/::' is invalid. 89 * DETAILS=The following 1 violations have been reported: 90 * Invalid Property 91 * What has happened? 92 * Summary: Value '9,999' (Integer) for property 'tutorial.property.constraints.storyPoint' 93 * is invalid. The definition is provided by 'inmemory:31456826-29d7-4e5c-a120-a06e847bb4f4'. 94 * Details: The violation report: 95 * Select one of the following values: 0, 1, 2, 3, 5, 8, 13, 21, 50, 100 96 * What does this implicate for the task having encountered this problem? 97 * The system cannot use the property value since it is invalid. 98 * What to do now? 99 * Please correct the value according to the violation report. 100 * The following 1 mandatory properties have not been provided: 101 * tutorial.property.constraints.storyPoint 102 * No unknown properties have been found. 103 * IMPLICATIONS_ON_CURRENT_TASK=The invalid configuration ':/::' cannot be used. 104 * WHAT_TO_DO_NOW=Please check your configuration. Remove violations, add 105 * mandatory properties and remove unknown properties. Maybe some 106 * properties have been misspelled?" 107 * </pre> 108 * <p> 109 * As you can see, the message shows that a validation has been recognized and 110 * the detailed validation message for the validation is shown. 111 * </p> 112 * <p> 113 * You may ask "why is it allowed to set an invalid value?". The reason for 114 * this is that we do not validate a value that is set because it may rely on 115 * properties that are not yet set or will never be set in the configuration. 116 * For instance if you assign a placeholder expression, but the placeholder is 117 * not set in the current configuration. If you want your client to 118 * immediately check the property, simply fetch it with resolving. Maybe we 119 * provide a method that only set a valid property in the future. 120 * </p> 121 */ 122 @Test(expected = ConfigurationValidationException.class) 123 @DocChapter 124 public void rangedValueProperty() 125 { 126 try 127 { 128 config.setProperty(properties.storyPointPropertyKey(), "9999"); 129 config.validate(); 130 } 131 catch (final ConfigurationValidationException e) 132 { 133 final Messages messages = e.createMessages(Locale.ENGLISH); 134 final String messagesText = messages.toString(); 135 // Locale is used in submessage and submessage is displayed: 136 assertThat( 137 messagesText, 138 allOf( 139 containsString("What has happened?"), 140 containsString("Select one of the following values: 0, 1, 2, 3, 5, 8, 13, 21, 50, 100"))); 141 throw e; 142 } 143 } 144 145 /** 146 * <p> 147 * The next example shows how violations of constrains defined with <a 148 * href="http://beanvalidation.org/">Bean Validation</a> are displayed. 149 * </p> 150 * <p> 151 * The property is declared as follows: 152 * </p> 153 * 154 * <pre> 155 * @PropertyExpression("0") 156 * @Min(0) 157 * @PropertyLifecycle(access = AccessType.READ_WRITE) 158 * int happiness(); 159 * </pre> 160 * 161 * {@insertCode} 162 * <p> 163 * The resulting message is: 164 * </p> 165 * 166 * <pre> 167 * TITLE=Configuration Problem 168 * SUMMARY=Configuration ':/::' is invalid. 169 * DETAILS=The following 1 violations have been reported: 170 * Invalid Property 171 * What has happened? 172 * Summary: Value '-1' (Integer) for property 173 * 'tutorial.property.constraints.happiness' is invalid. The definition 174 * is provided by 'inmemory:6c100657-9187-41e0-b221-732e20c78d01'. 175 * Details: The violation report: 176 * Min(value=0) 177 * What does this implicate for the task having encountered this problem? 178 * The system cannot use the property value since it is invalid. 179 * What to do now? 180 * Please correct the value according to the violation report. 181 * No mandatory properties are missing. 182 * No unknown properties have been found. 183 * IMPLICATIONS_ON_CURRENT_TASK=The invalid configuration ':/::' cannot be used. 184 * WHAT_TO_DO_NOW=Please check your configuration. Remove violations, add 185 * mandatory properties and remove unknown properties. Maybe some 186 * properties have been misspelled?" 187 * </pre> 188 */ 189 @Test(expected = ConfigurationValidationException.class) 190 @DocChapter 191 public void beanValidationConstraints() 192 { 193 try 194 { 195 config.setProperty(properties.happinessPropertyKey(), "-1"); 196 config.validate(); 197 } 198 catch (final ConfigurationValidationException e) 199 { 200 final Messages messages = e.createMessages(Locale.ENGLISH); 201 final String messagesText = messages.toString(); 202 assertThat( 203 messagesText, 204 allOf(containsString("Value '-1' (Integer) for property"), 205 containsString("must be greater than or equal to 0"))); 206 throw e; 207 } 208 } 209 210 /** 211 * <p> 212 * Now we come to custom constraints. 213 * </p> 214 * <p> 215 * The property is declared as follows: 216 * </p> 217 * 218 * <pre> 219 * @PropertyExpression("0") 220 * @RealHappiness 221 * @PropertyLifecycle(access = AccessType.READ_WRITE) 222 * int realHappiness(); 223 * </pre> 224 * <p> 225 * We test the constraint: 226 * </p> 227 * {@insertCode} 228 * <p> 229 * The resulting message is: 230 * </p> 231 * 232 * <pre> 233 * TITLE=Configuration Problem 234 SUMMARY=Configuration ':/::' is invalid. 235 DETAILS=The following 1 violations have been reported: 236 Invalid Property 237 What has happened? 238 Summary: Value '42' (Integer) for property 239 'tutorial.property.constraints.realHappiness' is invalid. The 240 definition is provided by 'inmemory:f7e57216-341d-4505-951f-a172aebbe9c0'. 241 Details: The violation report: 242 Your happiness level is out of range! 243 What does this implicate for the task having encountered this problem? 244 The system cannot use the property value since it is invalid. 245 What to do now? 246 Please correct the value according to the violation report. 247 248 No mandatory properties are missing. 249 No unknown properties have been found. 250 IMPLICATIONS_ON_CURRENT_TASK=The invalid configuration ':/::' cannot be used. 251 WHAT_TO_DO_NOW=Please check your configuration. Remove violations, add 252 mandatory properties and remove unknown properties. Maybe some 253 properties have been misspelled? 254 * </pre> 255 */ 256 @Test(expected = ConfigurationValidationException.class) 257 @DocChapter 258 public void customConstraints() 259 { 260 try 261 { 262 config.setProperty(properties.realHappinessPropertyKey(), "42"); 263 config.validate(); 264 } 265 catch (final ConfigurationValidationException e) 266 { 267 final Messages messages = e.createMessages(Locale.ENGLISH); 268 final String messagesText = messages.toString(); 269 assertThat(messagesText, 270 containsString("Your happiness level is out of range!")); 271 throw e; 272 } 273 } 274 }