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 }