View Javadoc

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     * &#064;PropertyIntValueRange(value = { 0, 1, 2, 3, 5, 8, 13, 21, 50, 100 },
74     *     defaultValue = 8)
75     * &#064;NotNull
76     * &#064;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    * &#064;PropertyExpression(&quot;0&quot;)
156    * &#064;Min(0)
157    * &#064;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 }