001 /* 002 * CDDL HEADER START 003 * 004 * The contents of this file are subject to the terms of the 005 * Common Development and Distribution License, Version 1.0 only 006 * (the "License"). You may not use this file except in compliance 007 * with the License. 008 * 009 * You can obtain a copy of the license at 010 * trunk/opends/resource/legal-notices/OpenDS.LICENSE 011 * or https://OpenDS.dev.java.net/OpenDS.LICENSE. 012 * See the License for the specific language governing permissions 013 * and limitations under the License. 014 * 015 * When distributing Covered Code, include this CDDL HEADER in each 016 * file and include the License file at 017 * trunk/opends/resource/legal-notices/OpenDS.LICENSE. If applicable, 018 * add the following below this CDDL HEADER, with the fields enclosed 019 * by brackets "[]" replaced with your own identifying information: 020 * Portions Copyright [yyyy] [name of copyright owner] 021 * 022 * CDDL HEADER END 023 * 024 * 025 * Copyright 2006-2008 Sun Microsystems, Inc. 026 */ 027 package org.opends.server.backends.jeb; 028 029 import com.sleepycat.je.EnvironmentConfig; 030 031 import org.opends.server.config.ConfigConstants; 032 import org.opends.server.config.ConfigException; 033 import org.opends.server.types.DebugLogLevel; 034 035 import java.util.HashMap; 036 import java.util.Map; 037 import java.lang.reflect.Method; 038 import java.util.HashSet; 039 import java.util.SortedSet; 040 import java.util.StringTokenizer; 041 import java.util.List; 042 import java.util.Arrays; 043 044 import org.opends.messages.Message; 045 046 import org.opends.server.loggers.debug.DebugTracer; 047 import org.opends.server.admin.std.server.LocalDBBackendCfg; 048 import org.opends.server.admin.std.meta.LocalDBBackendCfgDefn; 049 import org.opends.server.admin.DurationPropertyDefinition; 050 import org.opends.server.admin.BooleanPropertyDefinition; 051 import org.opends.server.admin.PropertyDefinition; 052 053 import static org.opends.server.loggers.debug.DebugLogger.*; 054 import static org.opends.messages.ConfigMessages.*; 055 056 /** 057 * This class maps JE properties to configuration attributes. 058 */ 059 public class ConfigurableEnvironment 060 { 061 /** 062 * The tracer object for the debug logger. 063 */ 064 private static final DebugTracer TRACER = getTracer(); 065 066 /** 067 * The name of the attribute which configures the database cache size as a 068 * percentage of Java VM heap size. 069 */ 070 public static final String ATTR_DATABASE_CACHE_PERCENT = 071 ConfigConstants.NAME_PREFIX_CFG + "db-cache-percent"; 072 073 /** 074 * The name of the attribute which configures the database cache size as an 075 * approximate number of bytes. 076 */ 077 public static final String ATTR_DATABASE_CACHE_SIZE = 078 ConfigConstants.NAME_PREFIX_CFG + "db-cache-size"; 079 080 /** 081 * The name of the attribute which configures whether data updated by a 082 * database transaction is forced to disk. 083 */ 084 public static final String ATTR_DATABASE_TXN_NO_SYNC = 085 ConfigConstants.NAME_PREFIX_CFG + "db-txn-no-sync"; 086 087 /** 088 * The name of the attribute which configures whether data updated by a 089 * database transaction is written from the Java VM to the O/S. 090 */ 091 public static final String ATTR_DATABASE_TXN_WRITE_NO_SYNC = 092 ConfigConstants.NAME_PREFIX_CFG + "db-txn-write-no-sync"; 093 094 /** 095 * The name of the attribute which configures whether the database background 096 * cleaner thread runs. 097 */ 098 public static final String ATTR_DATABASE_RUN_CLEANER = 099 ConfigConstants.NAME_PREFIX_CFG + "db-run-cleaner"; 100 101 /** 102 * The name of the attribute which configures the minimum percentage of log 103 * space that must be used in log files. 104 */ 105 public static final String ATTR_CLEANER_MIN_UTILIZATION = 106 ConfigConstants.NAME_PREFIX_CFG + "db-cleaner-min-utilization"; 107 108 /** 109 * The name of the attribute which configures the maximum size of each 110 * individual JE log file, in bytes. 111 */ 112 public static final String ATTR_DATABASE_LOG_FILE_MAX = 113 ConfigConstants.NAME_PREFIX_CFG + "db-log-file-max"; 114 115 /** 116 * The name of the attribute which configures the database cache eviction 117 * algorithm. 118 */ 119 public static final String ATTR_EVICTOR_LRU_ONLY = 120 ConfigConstants.NAME_PREFIX_CFG + "db-evictor-lru-only"; 121 122 /** 123 * The name of the attribute which configures the number of nodes in one scan 124 * of the database cache evictor. 125 */ 126 public static final String ATTR_EVICTOR_NODES_PER_SCAN = 127 ConfigConstants.NAME_PREFIX_CFG + "db-evictor-nodes-per-scan"; 128 129 130 /** 131 * The name of the attribute which configures whether the logging file 132 * handler will be on or off. 133 */ 134 public static final String ATTR_LOGGING_FILE_HANDLER_ON = 135 ConfigConstants.NAME_PREFIX_CFG + "db-logging-file-handler-on"; 136 137 138 /** 139 * The name of the attribute which configures the trace logging message level. 140 */ 141 public static final String ATTR_LOGGING_LEVEL = 142 ConfigConstants.NAME_PREFIX_CFG + "db-logging-level"; 143 144 145 /** 146 * The name of the attribute which configures how many bytes are written to 147 * the log before the checkpointer runs. 148 */ 149 public static final String ATTR_CHECKPOINTER_BYTES_INTERVAL = 150 ConfigConstants.NAME_PREFIX_CFG + "db-checkpointer-bytes-interval"; 151 152 153 /** 154 * The name of the attribute which configures the amount of time between 155 * runs of the checkpointer. 156 */ 157 public static final String ATTR_CHECKPOINTER_WAKEUP_INTERVAL = 158 ConfigConstants.NAME_PREFIX_CFG + 159 "db-checkpointer-wakeup-interval"; 160 161 162 /** 163 * The name of the attribute which configures the number of lock tables. 164 */ 165 public static final String ATTR_NUM_LOCK_TABLES = 166 ConfigConstants.NAME_PREFIX_CFG + "db-num-lock-tables"; 167 168 169 /** 170 * The name of the attribute which configures the number threads 171 * allocated by the cleaner for log file processing. 172 */ 173 public static final String ATTR_NUM_CLEANER_THREADS = 174 ConfigConstants.NAME_PREFIX_CFG + "db-num-cleaner-threads"; 175 176 177 /** 178 * The name of the attribute which may specify any native JE properties. 179 */ 180 public static final String ATTR_JE_PROPERTY = 181 ConfigConstants.NAME_PREFIX_CFG + "je-property"; 182 183 184 /** 185 * A map of JE property names to the corresponding configuration attribute. 186 */ 187 private static HashMap<String, String> attrMap = 188 new HashMap<String, String>(); 189 190 /** 191 * A map of configuration attribute names to the corresponding configuration 192 * object getter method. 193 */ 194 private static HashMap<String,Method> methodMap = 195 new HashMap<String, Method>(); 196 197 /** 198 * A map of configuration attribute names to the corresponding configuration 199 * PropertyDefinition. 200 */ 201 private static HashMap<String,PropertyDefinition> defnMap = 202 new HashMap<String, PropertyDefinition>(); 203 204 205 // Pulled from resource/admin/ABBREVIATIONS.xsl. db is mose common. 206 private static final List<String> ABBREVIATIONS = Arrays.asList(new String[] 207 {"aci", "ip", "ssl", "dn", "rdn", "jmx", "smtp", "http", 208 "https", "ldap", "ldaps", "ldif", "jdbc", "tcp", "tls", 209 "pkcs11", "sasl", "gssapi", "md5", "je", "dse", "fifo", 210 "vlv", "uuid", "md5", "sha1", "sha256", "sha384", "sha512", 211 "tls", "db"}); 212 213 /* 214 * e.g. db-cache-percent -> DBCachePercent 215 */ 216 private static String propNametoCamlCase(String hyphenated) 217 { 218 String[] components = hyphenated.split("\\-"); 219 StringBuilder buffer = new StringBuilder(); 220 for (String component: components) { 221 if (ABBREVIATIONS.contains(component)) { 222 buffer.append(component.toUpperCase()); 223 } else { 224 buffer.append(component.substring(0, 1).toUpperCase() + 225 component.substring(1)); 226 } 227 } 228 return buffer.toString(); 229 } 230 231 232 /** 233 * Register a JE property and its corresponding configuration attribute. 234 * 235 * @param propertyName The name of the JE property to be registered. 236 * @param attrName The name of the configuration attribute associated 237 * with the property. 238 * @throws Exception If there is an error in the attribute name. 239 */ 240 private static void registerProp(String propertyName, String attrName) 241 throws Exception 242 { 243 // Strip off NAME_PREFIX_CFG. 244 String baseName = attrName.substring(7); 245 246 String methodBaseName = propNametoCamlCase(baseName); 247 248 Class<LocalDBBackendCfg> configClass = LocalDBBackendCfg.class; 249 LocalDBBackendCfgDefn defn = LocalDBBackendCfgDefn.getInstance(); 250 Class<? extends LocalDBBackendCfgDefn> defClass = defn.getClass(); 251 252 PropertyDefinition propDefn = 253 (PropertyDefinition)defClass.getMethod("get" + methodBaseName + 254 "PropertyDefinition").invoke(defn); 255 256 String methodName; 257 if (propDefn instanceof BooleanPropertyDefinition) 258 { 259 methodName = "is" + methodBaseName; 260 } 261 else 262 { 263 methodName = "get" + methodBaseName; 264 } 265 266 defnMap.put(attrName, propDefn); 267 methodMap.put(attrName, configClass.getMethod(methodName)); 268 attrMap.put(propertyName, attrName); 269 } 270 271 272 /** 273 * Get the name of the configuration attribute associated with a JE property. 274 * @param jeProperty The name of the JE property. 275 * @return The name of the associated configuration attribute. 276 */ 277 public static String getAttributeForProperty(String jeProperty) 278 { 279 return attrMap.get(jeProperty); 280 } 281 282 /** 283 * Get the value of a JE property that is mapped to a configuration attribute. 284 * @param cfg The configuration containing the property values. 285 * @param attrName The conriguration attribute type name. 286 * @return The string value of the JE property. 287 */ 288 private static String getPropertyValue(LocalDBBackendCfg cfg, String attrName) 289 { 290 try 291 { 292 PropertyDefinition propDefn = defnMap.get(attrName); 293 Method method = methodMap.get(attrName); 294 295 if (propDefn instanceof DurationPropertyDefinition) 296 { 297 Long value = (Long)method.invoke(cfg); 298 299 // JE durations are in microseconds so we must convert. 300 DurationPropertyDefinition durationPropDefn = 301 (DurationPropertyDefinition)propDefn; 302 value = 1000*durationPropDefn.getBaseUnit().toMilliSeconds(value); 303 304 return String.valueOf(value); 305 } 306 else 307 { 308 Object value = method.invoke(cfg); 309 return String.valueOf(value); 310 } 311 } 312 catch (Exception e) 313 { 314 if (debugEnabled()) 315 { 316 TRACER.debugCaught(DebugLogLevel.ERROR, e); 317 } 318 return ""; 319 } 320 } 321 322 323 324 static 325 { 326 // Register the parameters that have JE property names. 327 try 328 { 329 registerProp("je.maxMemoryPercent", ATTR_DATABASE_CACHE_PERCENT); 330 registerProp("je.maxMemory", ATTR_DATABASE_CACHE_SIZE); 331 registerProp("je.cleaner.minUtilization", ATTR_CLEANER_MIN_UTILIZATION); 332 registerProp("je.env.runCleaner", ATTR_DATABASE_RUN_CLEANER); 333 registerProp("je.evictor.lruOnly", ATTR_EVICTOR_LRU_ONLY); 334 registerProp("je.evictor.nodesPerScan", ATTR_EVICTOR_NODES_PER_SCAN); 335 registerProp("je.log.fileMax", ATTR_DATABASE_LOG_FILE_MAX); 336 registerProp("java.util.logging.FileHandler.on", 337 ATTR_LOGGING_FILE_HANDLER_ON); 338 registerProp("java.util.logging.level", ATTR_LOGGING_LEVEL); 339 registerProp("je.checkpointer.bytesInterval", 340 ATTR_CHECKPOINTER_BYTES_INTERVAL); 341 registerProp("je.checkpointer.wakeupInterval", 342 ATTR_CHECKPOINTER_WAKEUP_INTERVAL); 343 registerProp("je.lock.nLockTables", ATTR_NUM_LOCK_TABLES); 344 registerProp("je.cleaner.threads", ATTR_NUM_CLEANER_THREADS); 345 } 346 catch (Exception e) 347 { 348 if (debugEnabled()) 349 { 350 TRACER.debugCaught(DebugLogLevel.ERROR, e); 351 } 352 } 353 } 354 355 356 357 /** 358 * Create a JE environment configuration with default values. 359 * 360 * @return A JE environment config containing default values. 361 */ 362 public static EnvironmentConfig defaultConfig() 363 { 364 EnvironmentConfig envConfig = new EnvironmentConfig(); 365 366 envConfig.setTransactional(true); 367 envConfig.setAllowCreate(true); 368 369 // This property was introduced in JE 3.0. Shared latches are now used on 370 // all internal nodes of the b-tree, which increases concurrency for many 371 // operations. 372 envConfig.setConfigParam("je.env.sharedLatches", "true"); 373 374 // This parameter was set to false while diagnosing a Berkeley DB JE bug. 375 // Normally cleansed log files are deleted, but if this is set false 376 // they are instead renamed from .jdb to .del. 377 envConfig.setConfigParam("je.cleaner.expunge", "true"); 378 379 return envConfig; 380 } 381 382 383 384 /** 385 * Parse a configuration associated with a JE environment and create an 386 * environment config from it. 387 * 388 * @param cfg The configuration to be parsed. 389 * @return An environment config instance corresponding to the config entry. 390 * @throws ConfigException If there is an error in the provided configuration 391 * entry. 392 */ 393 public static EnvironmentConfig parseConfigEntry(LocalDBBackendCfg cfg) 394 throws ConfigException 395 { 396 EnvironmentConfig envConfig = defaultConfig(); 397 398 // Handle the attributes that do not have a JE property. 399 envConfig.setTxnNoSync(cfg.isDBTxnNoSync()); 400 envConfig.setTxnWriteNoSync(cfg.isDBTxnWriteNoSync()); 401 402 // Iterate through the config attributes associated with a JE property. 403 for (Map.Entry<String, String> mapEntry : attrMap.entrySet()) 404 { 405 String jeProperty = mapEntry.getKey(); 406 String attrName = mapEntry.getValue(); 407 408 String value = getPropertyValue(cfg, attrName); 409 envConfig.setConfigParam(jeProperty, value); 410 } 411 412 // See if there are any native JE properties specified in the config 413 // and if so try to parse, evaluate and set them. 414 SortedSet<String> jeProperties = cfg.getJEProperty(); 415 try { 416 envConfig = setJEProperties(envConfig, jeProperties, attrMap); 417 } catch (ConfigException e) { 418 throw e; 419 } 420 421 return envConfig; 422 } 423 424 425 426 /** 427 * Parse, validate and set native JE environment properties for 428 * a given environment config. 429 * 430 * @param envConfig The JE environment config for which to set 431 * the properties. 432 * @param jeProperties The JE environment properties to parse, 433 * validate and set. 434 * @param configAttrMap Component supported JE properties to 435 * their configuration attributes map. 436 * @return An environment config instance with given properties 437 * set. 438 * @throws ConfigException If there is an error while parsing, 439 * validating and setting any of the properties provided. 440 */ 441 public static EnvironmentConfig setJEProperties(EnvironmentConfig envConfig, 442 SortedSet<String> jeProperties, HashMap<String, String> configAttrMap) 443 throws ConfigException 444 { 445 if (jeProperties.isEmpty()) { 446 // return default config. 447 return envConfig; 448 } 449 450 // Set to catch duplicate properties. 451 HashSet<String> uniqueJEProperties = new HashSet<String>(); 452 453 // Iterate through the config values associated with a JE property. 454 for (String jeEntry : jeProperties) 455 { 456 StringTokenizer st = new StringTokenizer(jeEntry, "="); 457 if (st.countTokens() == 2) { 458 String jePropertyName = st.nextToken(); 459 String jePropertyValue = st.nextToken(); 460 // Check if it is a duplicate. 461 if (uniqueJEProperties.contains(jePropertyName)) { 462 Message message = ERR_CONFIG_JE_DUPLICATE_PROPERTY.get( 463 jePropertyName); 464 throw new ConfigException(message); 465 } 466 // Set JE property. 467 try { 468 envConfig.setConfigParam(jePropertyName, jePropertyValue); 469 // This is a special case that JE cannot validate before 470 // actually setting it. Validate it before it gets to JE. 471 if (jePropertyName.equals("java.util.logging.level")) { 472 java.util.logging.Level.parse(jePropertyValue); 473 } 474 // If this property shadows an existing config attribute. 475 if (configAttrMap.containsKey(jePropertyName)) { 476 Message message = ERR_CONFIG_JE_PROPERTY_SHADOWS_CONFIG.get( 477 jePropertyName, attrMap.get(jePropertyName)); 478 throw new ConfigException(message); 479 } 480 // Add this property to unique set. 481 uniqueJEProperties.add(jePropertyName); 482 } catch(IllegalArgumentException e) { 483 if (debugEnabled()) { 484 TRACER.debugCaught(DebugLogLevel.ERROR, e); 485 } 486 Message message = 487 ERR_CONFIG_JE_PROPERTY_INVALID.get( 488 jeEntry, e.getMessage()); 489 throw new ConfigException(message, e.getCause()); 490 } 491 } else { 492 Message message = 493 ERR_CONFIG_JE_PROPERTY_INVALID_FORM.get(jeEntry); 494 throw new ConfigException(message); 495 } 496 } 497 498 return envConfig; 499 } 500 501 502 503 }