001/** 002 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. 003 * 004 * Copyright (c) 2005 Sun Microsystems Inc. All Rights Reserved 005 * 006 * The contents of this file are subject to the terms 007 * of the Common Development and Distribution License 008 * (the License). You may not use this file except in 009 * compliance with the License. 010 * 011 * You can obtain a copy of the License at 012 * https://opensso.dev.java.net/public/CDDLv1.0.html or 013 * opensso/legal/CDDLv1.0.txt 014 * See the License for the specific language governing 015 * permission and limitations under the License. 016 * 017 * When distributing Covered Code, include this CDDL 018 * Header Notice in each file and include the License file 019 * at opensso/legal/CDDLv1.0.txt. 020 * If applicable, add the following below the CDDL Header, 021 * with the fields enclosed by brackets [] replaced by 022 * your own identifying information: 023 * "Portions Copyrighted [year] [name of copyright owner]" 024 * 025 * $Id: SystemProperties.java,v 1.21 2009/10/12 17:55:06 alanchu Exp $ 026 * 027 */ 028 029/* 030 * Portions Copyrighted 2010-2013 ForgeRock, Inc. 031 */ 032package com.iplanet.am.util; 033 034import com.iplanet.sso.SSOToken; 035import com.sun.identity.common.configuration.ServerConfiguration; 036import com.sun.identity.security.AdminTokenAction; 037import com.sun.identity.common.AttributeStruct; 038import com.sun.identity.common.PropertiesFinder; 039import com.sun.identity.shared.Constants; 040import com.sun.identity.sm.SMSEntry; 041import org.forgerock.openam.cts.api.CoreTokenConstants; 042 043import java.io.ByteArrayOutputStream; 044import java.io.FileInputStream; 045import java.io.IOException; 046import java.io.PrintStream; 047import java.net.InetAddress; 048import java.security.AccessController; 049import java.util.Enumeration; 050import java.util.HashMap; 051import java.util.HashSet; 052import java.util.Iterator; 053import java.util.Map; 054import java.util.MissingResourceException; 055import java.util.Properties; 056import java.util.ResourceBundle; 057import java.util.Set; 058import java.util.concurrent.locks.ReentrantReadWriteLock; 059 060/** 061 * This class provides functionality that allows single-point-of-access to all 062 * related system properties. 063 * <p> 064 * The system properties can be set in couple of ways: programmatically by 065 * calling the <code>initializeProperties</code> method, or can be statically 066 * loaded at startup from a file named: 067 * <code>AMConfig.[class,properties]</code>. 068 * Setting the properties through the API takes precedence and will replace the 069 * properties loaded via file. For statically loading the properties via a file, 070 * this class tries to first find a class, <code>AMConfig.class</code>, and 071 * then a file, <code>AMConfig.properties</code> in the CLASSPATH accessible 072 * to this code. The <code>AMConfig.class</code> takes precedence over the 073 * flat file <code>AMConfig.properties</code>. 074 * <p> 075 * If multiple servers are running, each may have their own configuration file. 076 * The naming convention for such scenarios is 077 * <code>AMConfig-<serverName></code>. 078 * @supported.all.api 079 */ 080public class SystemProperties { 081 private static String instanceName; 082 private static ReentrantReadWriteLock rwLock = new 083 ReentrantReadWriteLock(); 084 private static Map attributeMap = new HashMap(); 085 private static boolean sitemonitorDisabled = false; 086 private final static String TRUE = "true"; 087 088 static { 089 initAttributeMapping(); 090 } 091 092 private static void initAttributeMapping() { 093 try { 094 ResourceBundle rb = ResourceBundle.getBundle("serverAttributeMap"); 095 for (Enumeration e = rb.getKeys(); e.hasMoreElements(); ) { 096 String propertyName = (String)e.nextElement(); 097 attributeMap.put(propertyName, new AttributeStruct( 098 rb.getString(propertyName))); 099 } 100 } catch(java.util.MissingResourceException mse) { 101 // No Resource Bundle Found, Continue. 102 // Could be in Test Mode. 103 } 104 } 105 106 107 private static Properties props; 108 109 private static long lastModified; 110 111 private static String initError; 112 113 private static String initSecondaryError; 114 115 private static final String SERVER_NAME_PROPERTY = "server.name"; 116 117 private static final String CONFIG_NAME_PROPERTY = "amconfig"; 118 119 private static final String AMCONFIG_FILE_NAME = "AMConfig"; 120 121 /** 122 * Runtime flag to be set, in order to override the path of the 123 * configuration file. 124 */ 125 public static final String CONFIG_PATH = "com.iplanet.services.configpath"; 126 127 /** 128 * Default name of the configuration file. 129 */ 130 public static final String CONFIG_FILE_NAME = "serverconfig.xml"; 131 132 /** 133 * New configuration file extension 134 */ 135 public static final String PROPERTIES = "properties"; 136 137 public static final String NEWCONFDIR = "NEW_CONF_DIR"; 138 139 private static Map mapTagswap = new HashMap(); 140 private static Map tagswapValues; 141 142 /** 143 * Initialization to load the properties file for config information before 144 * anything else starts. 145 */ 146 static { 147 mapTagswap.put("%SERVER_PORT%", Constants.AM_SERVER_PORT); 148 mapTagswap.put("%SERVER_URI%", Constants.AM_SERVICES_DEPLOYMENT_DESCRIPTOR); 149 mapTagswap.put("%SERVER_HOST%", Constants.AM_SERVER_HOST); 150 mapTagswap.put("%SERVER_PROTO%", Constants.AM_SERVER_PROTOCOL); 151 mapTagswap.put("%BASE_DIR%", CONFIG_PATH); 152 mapTagswap.put("%SESSION_ROOT_SUFFIX%", 153 CoreTokenConstants.SYS_PROPERTY_SESSION_HA_REPOSITORY_ROOT_SUFFIX); 154 mapTagswap.put("%SESSION_STORE_TYPE%", 155 CoreTokenConstants.SYS_PROPERTY_SESSION_HA_REPOSITORY_TYPE); 156 157 try { 158 // Initialize properties 159 props = new Properties(); 160 161 // Load properties from file 162 String serverName = System.getProperty(SERVER_NAME_PROPERTY); 163 String configName = System.getProperty(CONFIG_NAME_PROPERTY, 164 AMCONFIG_FILE_NAME); 165 String fname = null; 166 FileInputStream fis = null; 167 if (serverName != null) { 168 serverName = serverName.replace('.', '_'); 169 fname = configName + "-" + serverName; 170 } else { 171 fname = configName; 172 } 173 initializeProperties(fname); 174 175 // Get the location of the new configuration file in case 176 // of single war deployment 177 try { 178 String newConfigFileLoc = props 179 .getProperty(Constants.AM_NEW_CONFIGFILE_PATH); 180 if ((newConfigFileLoc != null) && 181 (newConfigFileLoc.length() > 0) && 182 !newConfigFileLoc.equals(NEWCONFDIR) 183 ) { 184 String hostName = InetAddress.getLocalHost().getHostName() 185 .toLowerCase(); 186 String serverURI = props.getProperty( 187 Constants.AM_SERVICES_DEPLOYMENT_DESCRIPTOR); 188 serverURI = serverURI.replace('/', '_').toLowerCase(); 189 StringBuilder fileName = new StringBuilder(); 190 fileName.append(newConfigFileLoc).append("/").append( 191 AMCONFIG_FILE_NAME).append(serverURI).append( 192 hostName).append( 193 props.getProperty(Constants.AM_SERVER_PORT)) 194 .append(".").append(PROPERTIES); 195 Properties modProp = new Properties(); 196 try { 197 fis = new FileInputStream(fileName.toString()); 198 modProp.load(fis); 199 props.putAll(modProp); 200 } catch (IOException ioe) { 201 StringBuilder fileNameOrig = new StringBuilder(); 202 fileNameOrig.append(newConfigFileLoc).append("/") 203 .append(AMCONFIG_FILE_NAME).append(".").append( 204 PROPERTIES); 205 try { 206 fis = new FileInputStream(fileNameOrig.toString()); 207 modProp.load(fis); 208 props.putAll(modProp); 209 } catch (IOException ioexp) { 210 saveException(ioexp); 211 } 212 } finally { 213 if (fis != null) { 214 fis.close(); 215 } 216 } 217 } 218 } catch (Exception ex) { 219 saveException(ex); 220 } 221 } catch (MissingResourceException e) { 222 // Can't print the message to debug due to dependency 223 // Save it as a String and provide when requested. 224 ByteArrayOutputStream baos = new ByteArrayOutputStream(); 225 e.printStackTrace(new PrintStream(baos)); 226 initError = baos.toString(); 227 try { 228 baos.close(); 229 } catch (IOException ioe) { 230 // Should not happend, ignore the exception 231 } 232 } 233 sitemonitorDisabled = Boolean.valueOf(getProp( 234 Constants.SITEMONITOR_DISABLED, "false")).booleanValue(); 235 } 236 237 /** 238 * Helper function to handle associated exceptions during initialization of 239 * properties using external properties file in a single war deployment. 240 */ 241 static void saveException(Exception ex) { 242 // Save it as a String and provide when requested. 243 ByteArrayOutputStream baos = new ByteArrayOutputStream(); 244 ex.printStackTrace(new PrintStream(baos)); 245 initSecondaryError = baos.toString(); 246 try { 247 baos.close(); 248 } catch (IOException ioe) { 249 // Should not happend, ignore the exception 250 } 251 } 252 253 /** 254 * This method lets you query for a system property whose value is same as 255 * <code>String</code> key. The method first tries to read the property 256 * from java.lang.System followed by a lookup in the config file. 257 * 258 * @param key 259 * type <code>String</code>, the key whose value one is 260 * looking for. 261 * @return the value if the key exists; otherwise returns <code>null</code> 262 */ 263 public static String get(String key) { 264 rwLock.readLock().lock(); 265 266 try { 267 String answer = null; 268 269 // look up values in SMS services only if in server mode. 270 if (isServerMode() || sitemonitorDisabled) { 271 AttributeStruct ast = (AttributeStruct) attributeMap.get(key); 272 if (ast != null) { 273 answer = PropertiesFinder.getProperty(key, ast); 274 } 275 } 276 277 if (answer == null) { 278 answer = getProp(key); 279 280 if ((answer != null) && (tagswapValues != null)) { 281 Set set = new HashSet(); 282 set.addAll(tagswapValues.keySet()); 283 284 for (Iterator i = set.iterator(); i.hasNext();) { 285 String k = (String) i.next(); 286 String val = (String) tagswapValues.get(k); 287 288 if (k.equals("%SERVER_URI%")) { 289 if ((val != null) && (val.length() > 0)) { 290 if (val.charAt(0) == '/') { 291 answer = answer.replaceAll("/%SERVER_URI%", 292 val); 293 String lessSlash = val.substring(1); 294 answer = answer.replaceAll("%SERVER_URI%", 295 lessSlash); 296 } else { 297 answer = answer.replaceAll(k, val); 298 } 299 } 300 } else { 301 answer = answer.replaceAll(k, val); 302 } 303 } 304 305 if (answer.indexOf("%ROOT_SUFFIX%") != -1) { 306 answer = answer.replaceAll("%ROOT_SUFFIX%", 307 SMSEntry.getAMSdkBaseDN()); 308 } 309 } 310 } 311 312 313 return (answer); 314 } finally { 315 rwLock.readLock().unlock(); 316 } 317 } 318 319 private static String getProp(String key, String def) { 320 String value = getProp(key); 321 return ((value == null) ? def : value); 322 } 323 324 private static String getProp(String key) { 325 String answer = System.getProperty(key); 326 if (answer == null) { 327 answer = props.getProperty(key); 328 } 329 return answer; 330 } 331 332 /** 333 * This method lets you query for a system property whose value is same as 334 * <code>String</code> key. 335 * 336 * @param key the key whose value one is looking for. 337 * @param def the default value if the key does not exist. 338 * @return the value if the key exists; otherwise returns default value. 339 */ 340 public static String get(String key, String def) { 341 String value = get(key); 342 return ((value == null) ? def : value); 343 } 344 345 /** 346 * Returns the property value as a boolean 347 * 348 * @param key the key whose value one is looking for. 349 * @return the boolean value if the key exists; otherwise returns false 350 */ 351 public static boolean getAsBoolean(String key) { 352 String value = get(key); 353 354 if (value == null) 355 return false; 356 357 return (value.equalsIgnoreCase(TRUE) ? true : false); 358 } 359 360 /** 361 * Returns the property value as a boolean 362 * 363 * @param key 364 * @param defaultValue value if key is not found. 365 * @return the boolean value if the key exists; otherwise the default value 366 */ 367 public static boolean getAsBoolean(String key, boolean defaultValue) { 368 String value = get(key); 369 370 if (value == null) 371 { return defaultValue; } 372 373 return (value.equalsIgnoreCase(TRUE) ? true : false); 374 } 375 376 /** 377 * Returns all the properties defined and their values. 378 * 379 * @return Properties object with all the key value pairs. 380 */ 381 public static Properties getProperties() { 382 rwLock.readLock().lock(); 383 try { 384 Properties properties = new Properties(); 385 properties.putAll(props); 386 return properties; 387 } finally { 388 rwLock.readLock().unlock(); 389 } 390 } 391 392 /** 393 * This method lets you get all the properties defined and their values. The 394 * method first tries to load the properties from java.lang.System followed 395 * by a lookup in the config file. 396 * 397 * @return Properties object with all the key value pairs. 398 * 399 */ 400 public static Properties getAll() { 401 rwLock.readLock().lock(); 402 403 try { 404 Properties properties = new Properties(); 405 properties.putAll(props); 406 // Iterate over the System Properties & add them in result obj 407 Iterator it = System.getProperties().entrySet().iterator(); 408 while (it.hasNext()) { 409 Map.Entry entry = (Map.Entry) it.next(); 410 String key = (String) entry.getKey(); 411 String val = (String) entry.getValue(); 412 if ((key != null) && (key.length() > 0)) { 413 properties.setProperty(key, val); 414 } 415 } 416 return properties; 417 } finally { 418 rwLock.readLock().unlock(); 419 } 420 } 421 422 /** 423 * This method lets you query for all the platform properties defined and 424 * their values. Returns a Properties object with all the key value pairs. 425 * 426 * @deprecated use <code>getAll()</code> 427 * 428 * @return the platform properties 429 */ 430 public static Properties getPlatform() { 431 return getAll(); 432 } 433 434 private static void updateTagswapMap(Properties properties) { 435 tagswapValues = new HashMap(); 436 for (Iterator i = mapTagswap.keySet().iterator(); i.hasNext(); ) { 437 String key = (String)i.next(); 438 String rgKey = (String)mapTagswap.get(key); 439 String val = System.getProperty(rgKey); 440 if (val == null) { 441 val = (String)properties.get(rgKey); 442 } 443 tagswapValues.put(key, val); 444 } 445 } 446 447 /** 448 * Initializes properties bundle from the <code>file<code> 449 * passed. 450 * 451 * @param file type <code>String</code>, file name for the resource bundle 452 * @exception MissingResourceException 453 */ 454 public static void initializeProperties(String file) 455 throws MissingResourceException { 456 rwLock.writeLock().lock(); 457 try { 458 ResourceBundle bundle = ResourceBundle.getBundle(file); 459 // Copy the properties to props 460 Enumeration e = bundle.getKeys(); 461 Properties newProps = new Properties(); 462 newProps.putAll(props); 463 while (e.hasMoreElements()) { 464 String key = (String) e.nextElement(); 465 newProps.put(key, bundle.getString(key)); 466 } 467 // Reset the last modified time 468 props = newProps; 469 updateTagswapMap(props); 470 lastModified = System.currentTimeMillis(); 471 } finally { 472 rwLock.writeLock().unlock(); 473 } 474 } 475 476 public static void initializeProperties(Properties properties){ 477 initializeProperties(properties, false); 478 } 479 480 /** 481 * Initializes the properties to be used by OpenSSO. Ideally this 482 * must be called first before any other method is called within OpenSSO 483 * Enterprise. This method provides a programmatic way to set the 484 * properties, and will override similar properties if loaded for a 485 * properties file. 486 * 487 * @param properties properties for OpenSSO 488 * @param reset <code>true</code> to reset existing properties. 489 */ 490 public static void initializeProperties( 491 Properties properties, 492 boolean reset) 493 { 494 initializeProperties(properties, reset, false); 495 } 496 497 /** 498 * Initializes the properties to be used by OpenSSO. Ideally this 499 * must be called first before any other method is called within OpenSSO 500 * Enterprise. This method provides a programmatic way to set the 501 * properties, and will override similar properties if loaded for a 502 * properties file. 503 * 504 * @param properties properties for OpenSSO. 505 * @param reset <code>true</code> to reset existing properties. 506 * @param withDefaults <code>true</code> to include default properties. 507 */ 508 public static void initializeProperties( 509 Properties properties, 510 boolean reset, 511 boolean withDefaults) { 512 Properties defaultProp = null; 513 if (withDefaults) { 514 SSOToken appToken = (SSOToken) AccessController.doPrivileged( 515 AdminTokenAction.getInstance()); 516 defaultProp = ServerConfiguration.getDefaults(appToken); 517 } 518 519 rwLock.writeLock().lock(); 520 521 try { 522 Properties newProps = new Properties(); 523 if (defaultProp != null) { 524 newProps.putAll(defaultProp); 525 } 526 527 528 if (!reset) { 529 newProps.putAll(props); 530 } 531 532 newProps.putAll(properties); 533 props = newProps; 534 updateTagswapMap(props); 535 lastModified = System.currentTimeMillis(); 536 } finally { 537 rwLock.writeLock().unlock(); 538 } 539 } 540 541 /** 542 * Initializes the property to be used by OpenSSO. Ideally this 543 * must be called first before any other method is called within OpenSSO 544 * Enterprise. 545 * This method provides a programmatic way to set a specific property, and 546 * will override similar property if loaded for a properties file. 547 * 548 * @param propertyName property name. 549 * @param propertyValue property value. 550 */ 551 public static void initializeProperties( 552 String propertyName, 553 String propertyValue 554 ) { 555 rwLock.writeLock().lock(); 556 557 try { 558 Properties newProps = new Properties(); 559 newProps.putAll(props); 560 newProps.put(propertyName, propertyValue); 561 props = newProps; 562 updateTagswapMap(props); 563 lastModified = System.currentTimeMillis(); 564 } finally { 565 rwLock.writeLock().unlock(); 566 } 567 } 568 569 /** 570 * Returns a counter for last modification. The counter is incremented if 571 * the properties are changed by calling the following method 572 * <code>initializeProperties</code>. This is a convenience methods for 573 * applications to track changes to OpenSSO properties. 574 * 575 * @return counter of the last modification 576 */ 577 public static long lastModified() { 578 return (lastModified); 579 } 580 581 /** 582 * Returns error messages during initialization, else <code>null</code>. 583 * 584 * @return error messages during initialization 585 */ 586 public static String getInitializationError() { 587 return (initError); 588 } 589 590 /** 591 * Returns error messages during initialization using the single war 592 * deployment, else <code>null</code>. 593 * 594 * @return error messages during initialization of AM as single war 595 */ 596 public static String getSecondaryInitializationError() { 597 return (initSecondaryError); 598 } 599 600 /** 601 * Sets the server instance name of which properties are retrieved 602 * to initialized this object. 603 * 604 * @param name Server instance name. 605 */ 606 public static void setServerInstanceName(String name) { 607 instanceName = name; 608 } 609 610 /** 611 * Returns the server instance name of which properties are retrieved 612 * to initialized this object. 613 * 614 * @return Server instance name. 615 */ 616 public static String getServerInstanceName() { 617 return instanceName; 618 } 619 620 /** 621 * Returns <code>true</code> if instance is running in server mode. 622 * 623 * @return <code>true</code> if instance is running in server mode. 624 */ 625 public static boolean isServerMode() { 626 // use getProp and not get method to avoid infinite loop 627 return Boolean.valueOf(getProp( 628 Constants.SERVER_MODE, "false")).booleanValue(); 629 } 630 631 /** 632 * Returns the property name to service attribute schema name mapping. 633 * 634 * @return Property name to service attribute schema name mapping. 635 */ 636 public static Map getAttributeMap() { 637 rwLock.readLock().lock(); 638 try { 639 return attributeMap; 640 } finally { 641 rwLock.readLock().unlock(); 642 } 643 } 644}