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 }