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.config.key;
17  
18  import static de.smartics.properties.impl.config.domain.key.envapp.EnvAppConfigurationKeyHelper.SYSTEM_PROPERTY_ENVIRONMENT_NAME;
19  import static de.smartics.properties.impl.config.domain.key.envapp.EnvAppConfigurationKeyHelper.SYSTEM_PROPERTY_ENVIRONMENT_NODE;
20  import static org.hamcrest.MatcherAssert.assertThat;
21  import static org.hamcrest.Matchers.is;
22  import static org.hamcrest.Matchers.not;
23  import static org.hamcrest.Matchers.nullValue;
24  
25  import org.junit.Test;
26  
27  import de.smartics.projectdoc.annotations.DocCategory;
28  import de.smartics.projectdoc.annotations.Document;
29  import de.smartics.projectdoc.annotations.topic.DocChapter;
30  import de.smartics.projectdoc.annotations.topic.DocSection;
31  import de.smartics.properties.api.config.app.ConfigurationPropertiesFactory;
32  import de.smartics.properties.api.config.app.ConfigurationPropertiesFactoryFactory;
33  import de.smartics.properties.api.config.domain.ConfigurationPropertiesManagement;
34  import de.smartics.properties.api.config.domain.key.ApplicationId;
35  import de.smartics.properties.api.config.domain.key.ConfigurationKey;
36  import de.smartics.properties.api.config.domain.key.EnvironmentId;
37  import de.smartics.properties.impl.config.domain.key.rtaware.TenantUserConfigurationKey;
38  import de.smartics.properties.impl.config.domain.key.rtaware.TenantUserConfigurationKeyHelper;
39  import de.smartics.sandbox.mail.MailProperties;
40  
41  /**
42   * This tutorial shows how configuration keys can be provided at runtime.
43   */
44  @Document(title = "Providing Configuration Keys", sortKey = "basics1000")
45  @DocCategory({ "basics" })
46  // @DocTopic(path="basics", step="1000")
47  public class ConfigurationKeyTutorial
48  {
49    /**
50     * The configuration is created by a {@link ConfigurationPropertiesFactory}
51     * that is configured via the service API. Therefore there is a file named
52     *
53     * <pre>
54     * META - INF / services
55     *     / de.smartics.properties.api.config.app.ConfigurationPropertiesFactory
56     * </pre>
57     *
58     * that contains the following information
59     *
60     * <pre>
61     * de.smartics.properties.impl.config.properties.PropertiesConfigurationPropertiesFactory
62     * </pre>
63     * <p>
64     * This example assumes that there are a couple of modules declared as
65     * dependencies. For the sake of simplicity we omit the version numbers.
66     * </p>
67     *
68     * <pre>
69     *     <dependency>
70     *       <groupId>de.smartics.sandbox</groupId>
71     *       <artifactId>test-module-mail</artifactId>
72     *     </dependency>
73     *     <dependency>
74     *       <groupId>de.smartics.sandbox</groupId>
75     *       <artifactId>test-module-announce</artifactId>
76     *     </dependency>
77     *     <dependency>
78     *       <groupId>de.smartics.sandbox</groupId>
79     *       <artifactId>test-module-agile</artifactId>
80     *     </dependency>
81     * </pre>
82     * <p>
83     * Each of these modules can be configured via properties files. The
84     * definition for these properties is provided by a dependency like this:
85     * </p>
86     *
87     * <pre>
88     *     <dependency>
89     *       <groupId>de.smartics.sandbox</groupId>
90     *       <artifactId>test-application-config</artifactId>
91     *     </dependency>
92     * </pre>
93     * <p>
94     * In real systems this dependency may be provided by a deployment process or
95     * a build process that bundles the dependency within a deploy- or installable
96     * unit. It is required to have this information accessible at runtime.
97     * <p>
98     * Now let's have a look how we can access information.
99     * </p>
100    * {@insertCode}
101    * <p>
102    * In {@$1} this default factory is created.
103    * </p>
104    * <p>
105    * To get access to the configuration provided for a specific environment, a
106    * configuration key has to be constructed. In its simplest form this is done
107    * by hand as shown in {@$2}. Note that the configuration key is
108    * constructed of two main parts:
109    * </p>
110    * <ol>
111    * <li>environment ID - which contains the name of the environment (
112    * <code>test</code> in our example) and an optional node information (
113    * <code>null</code> in our example).</li>
114    * <li>application ID - which contains a group name, an artifact name and a
115    * version information. This uniquely identifies the application that uses a
116    * configuration for its modules.
117    * </ol>
118    * <p>
119    * The key specified in {@$2} matches the key defined in the
120    * <code>definition.xml</code> of artifact
121    * <code>test-application-config</code> (referenced in the dependency block
122    * above).
123    * </p>
124    * <p>
125    * With this configuration key we are able to access the configuration as
126    * shown in {@$3}. The property set {@link MailProperties} is part of the
127    * module <code>test-module-mail</code>. As you may already know we simply
128    * access the property set by retrieving it from the configuration by its type
129    * name as shown in {@$4}.
130    * </p>
131    * <p>
132    * In {@$5} and {@$6} we only show that the mails-per-page property
133    * value is indeed accessible and shows the expected value.
134    * </p>
135    */
136   @DocChapter
137   @Test
138   public void accessConfigurationByHardcodedKey()
139   {
140     final ConfigurationPropertiesFactory factory =
141         ConfigurationPropertiesFactoryFactory.createDefaultFactory(); // {1}
142 
143     final ConfigurationKey<?> key =
144         new TenantUserConfigurationKey(new EnvironmentId("test"),
145             new ApplicationId("de.smartics.sandbox", "test-application",
146                 "0.1.0")); // {2}
147     final ConfigurationPropertiesManagement config =
148         factory.createManagement(key); // {3}
149 
150     final MailProperties properties =
151         config.getProperties(MailProperties.class); // {4}
152 
153     final int mailsPerPage = properties.mailsPerPage(); // {5}
154     assertThat(mailsPerPage, is(10)); // {6}
155   }
156 
157   /**
158    * <p>
159    * Hardcoding the key is usually not what you want. You may either retrieve
160    * the identity of the system by other means from the environment or let
161    * smartics-configuration determine it for you.
162    * </p>
163    * {@insertCode}
164    * <p>
165    * In {@$1} we instantiate the key helper and then load in {@$3} the
166    * relevant information from the environment by using a locator ({@$2})
167    * which is simply a class loaded from the class path of the application. This
168    * will access the meta information of the application found in
169    * <code>META-INF/MANIFEST.MF</code>. The environment part is determined by
170    * this formula:
171    * </p>
172    * <ol>
173    * <li>Check if the system property
174    * {@value de.smartics.properties.api.config.domain.key.TenantUserConfigurationKeyHelper#SYSTEM_PROPERTY_ENVIRONMENT_NAME}
175    * is set. If it is, use it and quit. If not proceed with the next step 3.</li>
176    * <li>Use <code>development</code> as environment.</li>
177    * <li>After the environment name is determined, the node name is calculated:
178    * Either the system property
179    * {@value de.smartics.properties.api.config.domain.key.TenantUserConfigurationKeyHelper#SYSTEM_PROPERTY_ENVIRONMENT_NODE}
180    * is specified or the canonical host name is derived.</li>
181    * <li>Now calculate the application information by reading the following
182    * information from the manifest file.
183    * <ol>
184    * <li>Group: {@value Attributes#Name#IMPLEMENTATION_VENDOR_ID}</li>
185    * <li>Artifact: {@value Attributes#Name#IMPLEMENTATION_TITLE}</li>
186    * <li>Version: {@value Attributes#Name#IMPLEMENTATION_VERSION}</li>
187    * </ol>
188    * </li>
189    * </ol>
190    * <p>
191    * If the manifest file is missing, an {@link IllegalStateException} is
192    * thrown.
193    * </p>
194    */
195   @DocChapter
196   @Test
197   public void determineSystemIdAutomatically()
198   {
199     final TenantUserConfigurationKeyHelper helper =
200         new TenantUserConfigurationKeyHelper(); // {1}
201     final Class<?> locator = ConfigurationKeyTutorial.class; // {2}
202     final TenantUserConfigurationKey key =
203         (TenantUserConfigurationKey) helper.load(locator); // {3}
204 
205     // {4}
206     assertThat(key.getEnvironmentId().getName(), is("development"));
207     assertThat(key.getEnvironmentId().getNode(), is(not(nullValue())));
208     assertThat(key.getApplicationId().getGroupId(),
209         is("de.smartics.properties"));
210     assertThat(key.getApplicationId().getArtifactId(),
211         is("smartics-properties-tutorial-config"));
212     assertThat(key.getApplicationId().getVersion(), is(not(nullValue())));
213   }
214 
215   /**
216    * <p>
217    * If you determine the name and node of the environment by other means, you
218    * may still use the automatic determination of the application.
219    * </p>
220    * {@insertCode}
221    */
222   @DocSection
223   @Test
224   public void specifyEnvironmentNameProgramatically()
225   {
226     final TenantUserConfigurationKeyHelper helper =
227         new TenantUserConfigurationKeyHelper();
228     final TenantUserConfigurationKey key =
229         (TenantUserConfigurationKey) helper.load("MyTest",
230             ConfigurationKeyTutorial.class); // {1}
231 
232     // {2}
233     assertThat(key.getEnvironmentId().getName(), is("MyTest"));
234     assertThat(key.getEnvironmentId().getNode(), is(not(nullValue())));
235   }
236 
237   /**
238    * <p>
239    * You may also provide the name of the environment and the node within this
240    * environment via system properties.
241    * </p>
242    * {@insertCode}
243    * <p>
244    * In {@$1} we set the system programatically. Usually this will be set
245    * on the command line via <code>-D</code>. In {@$2} we do not pass in an
246    * environment, since it is already set. This makes it easy to have the fall
247    * back option if no system property is set (as we have seen above, the
248    * defaults will be returned in that case). In {@$3} we see that the
249    * expected values are retrieved.
250    * </p>
251    */
252   @DocSection
253   @Test
254   public void specifyEnvironmentNameAndNodePerSystemProperty()
255   {
256     final String environment = "MyTest";
257     final String node = "MyNode";
258 
259     System.setProperty(SYSTEM_PROPERTY_ENVIRONMENT_NAME, environment); // {1}
260     System.setProperty(SYSTEM_PROPERTY_ENVIRONMENT_NODE, node);
261 
262     final TenantUserConfigurationKeyHelper helper =
263         new TenantUserConfigurationKeyHelper();
264     final TenantUserConfigurationKey key =
265         (TenantUserConfigurationKey) helper
266             .load(ConfigurationKeyTutorial.class); // {2}
267 
268     // {3}
269     assertThat(key.getEnvironmentId().getName(), is(environment));
270     assertThat(key.getEnvironmentId().getNode(), is(node));
271 
272     System.clearProperty(SYSTEM_PROPERTY_ENVIRONMENT_NAME);
273     System.clearProperty(SYSTEM_PROPERTY_ENVIRONMENT_NODE);
274   }
275 }