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.core;
028 import org.opends.messages.Message;
029
030
031
032 import java.lang.reflect.Method;
033 import java.util.ArrayList;
034 import java.util.Iterator;
035 import java.util.List;
036 import java.util.concurrent.ConcurrentHashMap;
037
038 import org.opends.server.admin.ClassPropertyDefinition;
039 import org.opends.server.admin.server.ConfigurationAddListener;
040 import org.opends.server.admin.server.ConfigurationChangeListener;
041 import org.opends.server.admin.server.ConfigurationDeleteListener;
042 import org.opends.server.admin.std.meta.ApproximateMatchingRuleCfgDefn;
043 import org.opends.server.admin.std.meta.EqualityMatchingRuleCfgDefn;
044 import org.opends.server.admin.std.meta.OrderingMatchingRuleCfgDefn;
045 import org.opends.server.admin.std.meta.SubstringMatchingRuleCfgDefn;
046 import org.opends.server.admin.std.server.ApproximateMatchingRuleCfg;
047 import org.opends.server.admin.std.server.EqualityMatchingRuleCfg;
048 import org.opends.server.admin.std.server.MatchingRuleCfg;
049 import org.opends.server.admin.std.server.OrderingMatchingRuleCfg;
050 import org.opends.server.admin.std.server.SubstringMatchingRuleCfg;
051 import org.opends.server.admin.std.server.RootCfg;
052 import org.opends.server.admin.server.ServerManagementContext;
053 import org.opends.server.api.ApproximateMatchingRule;
054 import org.opends.server.api.EqualityMatchingRule;
055 import org.opends.server.api.MatchingRule;
056 import org.opends.server.api.OrderingMatchingRule;
057 import org.opends.server.api.SubstringMatchingRule;
058 import org.opends.server.config.ConfigException;
059 import org.opends.server.types.AttributeType;
060 import org.opends.server.types.ConfigChangeResult;
061 import org.opends.server.types.DirectoryException;
062 import org.opends.server.types.DN;
063
064
065 import org.opends.server.types.InitializationException;
066 import org.opends.server.types.MatchingRuleUse;
067 import org.opends.server.types.ResultCode;
068
069 import static org.opends.messages.ConfigMessages.*;
070
071 import static org.opends.server.util.StaticUtils.*;
072 import org.opends.server.loggers.ErrorLogger;
073
074
075 /**
076 * This class defines a utility that will be used to manage the set of matching
077 * rules defined in the Directory Server. It wil initialize the rules when the
078 * server starts, and then will manage any additions, removals, or modifications
079 * to any matching rules while the server is running.
080 */
081 public class MatchingRuleConfigManager
082 implements ConfigurationChangeListener<MatchingRuleCfg>,
083 ConfigurationAddListener<MatchingRuleCfg>,
084 ConfigurationDeleteListener<MatchingRuleCfg>
085
086 {
087 // A mapping between the DNs of the config entries and the associated matching
088 // rules.
089 private ConcurrentHashMap<DN,MatchingRule> matchingRules;
090
091
092
093 /**
094 * Creates a new instance of this matching rule config manager.
095 */
096 public MatchingRuleConfigManager()
097 {
098 matchingRules = new ConcurrentHashMap<DN,MatchingRule>();
099 }
100
101
102
103 /**
104 * Initializes all matching rules currently defined in the Directory Server
105 * configuration. This should only be called at Directory Server startup.
106 *
107 * @throws ConfigException If a configuration problem causes the matching
108 * rule initialization process to fail.
109 *
110 * @throws InitializationException If a problem occurs while initializing
111 * the matching rules that is not related to
112 * the server configuration.
113 */
114 public void initializeMatchingRules()
115 throws ConfigException, InitializationException
116 {
117 // Get the root configuration object.
118 ServerManagementContext managementContext =
119 ServerManagementContext.getInstance();
120 RootCfg rootConfiguration =
121 managementContext.getRootConfiguration();
122
123
124 // Register as an add and delete listener with the root configuration so we
125 // can be notified if any matching rule entries are added or removed.
126 rootConfiguration.addMatchingRuleAddListener(this);
127 rootConfiguration.addMatchingRuleDeleteListener(this);
128
129
130 //Initialize the existing matching rules.
131 for (String name : rootConfiguration.listMatchingRules())
132 {
133 MatchingRuleCfg mrConfiguration = rootConfiguration.getMatchingRule(name);
134 mrConfiguration.addChangeListener(this);
135
136 if (mrConfiguration.isEnabled())
137 {
138 String className = mrConfiguration.getJavaClass();
139 try
140 {
141 MatchingRule matchingRule =
142 loadMatchingRule(className, mrConfiguration, true);
143
144 try
145 {
146 DirectoryServer.registerMatchingRule(matchingRule, false);
147 matchingRules.put(mrConfiguration.dn(), matchingRule);
148 }
149 catch (DirectoryException de)
150 {
151 Message message = WARN_CONFIG_SCHEMA_MR_CONFLICTING_MR.get(
152 String.valueOf(mrConfiguration.dn()), de.getMessageObject());
153 ErrorLogger.logError(message);
154 continue;
155 }
156 }
157 catch (InitializationException ie)
158 {
159 ErrorLogger.logError(ie.getMessageObject());
160 continue;
161 }
162 }
163 }
164 }
165
166
167
168 /**
169 * {@inheritDoc}
170 */
171 public boolean isConfigurationAddAcceptable(MatchingRuleCfg configuration,
172 List<Message> unacceptableReasons)
173 {
174 if (configuration.isEnabled())
175 {
176 // Get the name of the class and make sure we can instantiate it as a
177 // matching rule.
178 String className = configuration.getJavaClass();
179 try
180 {
181 loadMatchingRule(className, configuration, false);
182 }
183 catch (InitializationException ie)
184 {
185 unacceptableReasons.add(ie.getMessageObject());
186 return false;
187 }
188 }
189
190 // If we've gotten here, then it's fine.
191 return true;
192 }
193
194
195
196 /**
197 * {@inheritDoc}
198 */
199 public ConfigChangeResult applyConfigurationAdd(MatchingRuleCfg configuration)
200 {
201 ResultCode resultCode = ResultCode.SUCCESS;
202 boolean adminActionRequired = false;
203 ArrayList<Message> messages = new ArrayList<Message>();
204
205 configuration.addChangeListener(this);
206
207 if (! configuration.isEnabled())
208 {
209 return new ConfigChangeResult(resultCode, adminActionRequired, messages);
210 }
211
212 MatchingRule matchingRule = null;
213
214 // Get the name of the class and make sure we can instantiate it as a
215 // matching rule.
216 String className = configuration.getJavaClass();
217 try
218 {
219 matchingRule = loadMatchingRule(className, configuration, true);
220
221 try
222 {
223 DirectoryServer.registerMatchingRule(matchingRule, false);
224 matchingRules.put(configuration.dn(), matchingRule);
225 }
226 catch (DirectoryException de)
227 {
228 Message message = WARN_CONFIG_SCHEMA_MR_CONFLICTING_MR.get(
229 String.valueOf(configuration.dn()),
230 de.getMessageObject());
231 messages.add(message);
232
233 if (resultCode == ResultCode.SUCCESS)
234 {
235 resultCode = DirectoryServer.getServerErrorResultCode();
236 }
237 }
238 }
239 catch (InitializationException ie)
240 {
241 if (resultCode == ResultCode.SUCCESS)
242 {
243 resultCode = DirectoryServer.getServerErrorResultCode();
244 }
245
246 messages.add(ie.getMessageObject());
247 }
248
249 return new ConfigChangeResult(resultCode, adminActionRequired, messages);
250 }
251
252
253
254 /**
255 * {@inheritDoc}
256 */
257 public boolean isConfigurationDeleteAcceptable(MatchingRuleCfg configuration,
258 List<Message> unacceptableReasons)
259 {
260 // If the matching rule is enabled, then check to see if there are any
261 // defined attribute types or matching rule uses that use the matching rule.
262 // If so, then don't allow it to be deleted.
263 boolean configAcceptable = true;
264 MatchingRule matchingRule = matchingRules.get(configuration.dn());
265 if (matchingRule != null)
266 {
267 String oid = matchingRule.getOID();
268 for (AttributeType at : DirectoryServer.getAttributeTypes().values())
269 {
270 ApproximateMatchingRule amr = at.getApproximateMatchingRule();
271 if ((amr != null) && oid.equals(amr.getOID()))
272 {
273 Message message =
274 WARN_CONFIG_SCHEMA_CANNOT_DELETE_MR_IN_USE_BY_AT.get(
275 matchingRule.getName(),
276 at.getNameOrOID());
277 unacceptableReasons.add(message);
278
279 configAcceptable = false;
280 continue;
281 }
282
283 EqualityMatchingRule emr = at.getEqualityMatchingRule();
284 if ((emr != null) && oid.equals(emr.getOID()))
285 {
286 Message message =
287 WARN_CONFIG_SCHEMA_CANNOT_DELETE_MR_IN_USE_BY_AT.get(
288 matchingRule.getName(),
289 at.getNameOrOID());
290 unacceptableReasons.add(message);
291
292 configAcceptable = false;
293 continue;
294 }
295
296 OrderingMatchingRule omr = at.getOrderingMatchingRule();
297 if ((omr != null) && oid.equals(omr.getOID()))
298 {
299 Message message =
300 WARN_CONFIG_SCHEMA_CANNOT_DELETE_MR_IN_USE_BY_AT.get(
301 matchingRule.getName(),
302 at.getNameOrOID());
303 unacceptableReasons.add(message);
304
305 configAcceptable = false;
306 continue;
307 }
308
309 SubstringMatchingRule smr = at.getSubstringMatchingRule();
310 if ((smr != null) && oid.equals(smr.getOID()))
311 {
312 Message message =
313 WARN_CONFIG_SCHEMA_CANNOT_DELETE_MR_IN_USE_BY_AT.get(
314 matchingRule.getName(),
315 at.getNameOrOID());
316 unacceptableReasons.add(message);
317
318 configAcceptable = false;
319 continue;
320 }
321 }
322
323 for (MatchingRuleUse mru : DirectoryServer.getMatchingRuleUses().values())
324 {
325 if (oid.equals(mru.getMatchingRule().getOID()))
326 {
327 Message message =
328 WARN_CONFIG_SCHEMA_CANNOT_DELETE_MR_IN_USE_BY_MRU.get(
329 matchingRule.getName(),
330 mru.getName());
331 unacceptableReasons.add(message);
332
333 configAcceptable = false;
334 continue;
335 }
336 }
337 }
338
339 return configAcceptable;
340 }
341
342
343
344 /**
345 * {@inheritDoc}
346 */
347 public ConfigChangeResult applyConfigurationDelete(
348 MatchingRuleCfg configuration)
349 {
350 ResultCode resultCode = ResultCode.SUCCESS;
351 boolean adminActionRequired = false;
352 ArrayList<Message> messages = new ArrayList<Message>();
353
354 MatchingRule matchingRule = matchingRules.remove(configuration.dn());
355 if (matchingRule != null)
356 {
357 DirectoryServer.deregisterMatchingRule(matchingRule);
358 matchingRule.finalizeMatchingRule();
359 }
360
361 return new ConfigChangeResult(resultCode, adminActionRequired, messages);
362 }
363
364
365
366 /**
367 * {@inheritDoc}
368 */
369 public boolean isConfigurationChangeAcceptable(MatchingRuleCfg configuration,
370 List<Message> unacceptableReasons)
371 {
372 boolean configAcceptable = true;
373 if (configuration.isEnabled())
374 {
375 // Get the name of the class and make sure we can instantiate it as a
376 // matching rule.
377 String className = configuration.getJavaClass();
378 try
379 {
380 loadMatchingRule(className, configuration, false);
381 }
382 catch (InitializationException ie)
383 {
384 unacceptableReasons.add(ie.getMessageObject());
385 configAcceptable = false;
386 }
387 }
388 else
389 {
390 // If the matching rule is currently enabled and the change would make it
391 // disabled, then only allow it if the matching rule isn't already in use.
392 MatchingRule matchingRule = matchingRules.get(configuration.dn());
393 if (matchingRule != null)
394 {
395 String oid = matchingRule.getOID();
396 for (AttributeType at : DirectoryServer.getAttributeTypes().values())
397 {
398 ApproximateMatchingRule amr = at.getApproximateMatchingRule();
399 if ((amr != null) && oid.equals(amr.getOID()))
400 {
401 Message message =
402 WARN_CONFIG_SCHEMA_CANNOT_DISABLE_MR_IN_USE_BY_AT.get(
403 matchingRule.getName(),
404 at.getNameOrOID());
405 unacceptableReasons.add(message);
406
407 configAcceptable = false;
408 continue;
409 }
410
411 EqualityMatchingRule emr = at.getEqualityMatchingRule();
412 if ((emr != null) && oid.equals(emr.getOID()))
413 {
414 Message message =
415 WARN_CONFIG_SCHEMA_CANNOT_DISABLE_MR_IN_USE_BY_AT.get(
416 matchingRule.getName(),
417 at.getNameOrOID());
418 unacceptableReasons.add(message);
419
420 configAcceptable = false;
421 continue;
422 }
423
424 OrderingMatchingRule omr = at.getOrderingMatchingRule();
425 if ((omr != null) && oid.equals(omr.getOID()))
426 {
427 Message message =
428 WARN_CONFIG_SCHEMA_CANNOT_DISABLE_MR_IN_USE_BY_AT.get(
429 matchingRule.getName(),
430 at.getNameOrOID());
431 unacceptableReasons.add(message);
432
433 configAcceptable = false;
434 continue;
435 }
436
437 SubstringMatchingRule smr = at.getSubstringMatchingRule();
438 if ((smr != null) && oid.equals(smr.getOID()))
439 {
440 Message message = WARN_CONFIG_SCHEMA_CANNOT_DISABLE_MR_IN_USE_BY_AT
441 .get(matchingRule.getName(), at.getNameOrOID());
442 unacceptableReasons.add(message);
443
444 configAcceptable = false;
445 continue;
446 }
447 }
448
449 for (MatchingRuleUse mru :
450 DirectoryServer.getMatchingRuleUses().values())
451 {
452 if (oid.equals(mru.getMatchingRule().getOID()))
453 {
454 Message message =
455 WARN_CONFIG_SCHEMA_CANNOT_DISABLE_MR_IN_USE_BY_MRU.get(
456 matchingRule.getName(), mru.getName());
457 unacceptableReasons.add(message);
458
459 configAcceptable = false;
460 continue;
461 }
462 }
463 }
464 }
465
466 return configAcceptable;
467 }
468
469
470
471 /**
472 * {@inheritDoc}
473 */
474 public ConfigChangeResult applyConfigurationChange(
475 MatchingRuleCfg configuration)
476 {
477 ResultCode resultCode = ResultCode.SUCCESS;
478 boolean adminActionRequired = false;
479 ArrayList<Message> messages = new ArrayList<Message>();
480
481
482 // Get the existing matching rule if it's already enabled.
483 MatchingRule existingRule = matchingRules.get(configuration.dn());
484
485
486 // If the new configuration has the matching rule disabled, then disable it
487 // if it is enabled, or do nothing if it's already disabled.
488 if (! configuration.isEnabled())
489 {
490 if (existingRule != null)
491 {
492 DirectoryServer.deregisterMatchingRule(existingRule);
493
494 MatchingRule rule = matchingRules.remove(configuration.dn());
495 if (rule != null)
496 {
497 rule.finalizeMatchingRule();
498 }
499 }
500
501 return new ConfigChangeResult(resultCode, adminActionRequired, messages);
502 }
503
504
505 // Get the class for the matching rule. If the matching rule is already
506 // enabled, then we shouldn't do anything with it although if the class has
507 // changed then we'll at least need to indicate that administrative action
508 // is required. If the matching rule is disabled, then instantiate the
509 // class and initialize and register it as a matching rule.
510 String className = configuration.getJavaClass();
511 if (existingRule != null)
512 {
513 if (! className.equals(existingRule.getClass().getName()))
514 {
515 adminActionRequired = true;
516 }
517
518 return new ConfigChangeResult(resultCode, adminActionRequired, messages);
519 }
520
521 MatchingRule matchingRule = null;
522 try
523 {
524 matchingRule = loadMatchingRule(className, configuration, true);
525
526 try
527 {
528 DirectoryServer.registerMatchingRule(matchingRule, false);
529 matchingRules.put(configuration.dn(), matchingRule);
530 }
531 catch (DirectoryException de)
532 {
533 Message message = WARN_CONFIG_SCHEMA_MR_CONFLICTING_MR.get(
534 String.valueOf(configuration.dn()), de.getMessageObject());
535 messages.add(message);
536
537 if (resultCode == ResultCode.SUCCESS)
538 {
539 resultCode = DirectoryServer.getServerErrorResultCode();
540 }
541 }
542 }
543 catch (InitializationException ie)
544 {
545 if (resultCode == ResultCode.SUCCESS)
546 {
547 resultCode = DirectoryServer.getServerErrorResultCode();
548 }
549
550 messages.add(ie.getMessageObject());
551 }
552
553 return new ConfigChangeResult(resultCode, adminActionRequired, messages);
554 }
555
556
557
558 /**
559 * Loads the specified class, instantiates it as an attribute syntax, and
560 * optionally initializes that instance.
561 *
562 * @param className The fully-qualified name of the attribute syntax
563 * class to load, instantiate, and initialize.
564 * @param configuration The configuration to use to initialize the attribute
565 * syntax. It must not be {@code null}.
566 * @param initialize Indicates whether the matching rule instance should
567 * be initialized.
568 *
569 * @return The possibly initialized attribute syntax.
570 *
571 * @throws InitializationException If a problem occurred while attempting to
572 * initialize the attribute syntax.
573 */
574 private MatchingRule loadMatchingRule(String className,
575 MatchingRuleCfg configuration,
576 boolean initialize)
577 throws InitializationException
578 {
579 try
580 {
581 MatchingRule matchingRule = null;
582 if (configuration instanceof ApproximateMatchingRuleCfg)
583 {
584 ApproximateMatchingRuleCfgDefn definition =
585 ApproximateMatchingRuleCfgDefn.getInstance();
586 ClassPropertyDefinition propertyDefinition =
587 definition.getJavaClassPropertyDefinition();
588 Class<? extends ApproximateMatchingRule> approximateMatchingRuleClass =
589 propertyDefinition.loadClass(className,
590 ApproximateMatchingRule.class);
591 matchingRule = approximateMatchingRuleClass.newInstance();
592 }
593 else if (configuration instanceof EqualityMatchingRuleCfg)
594 {
595 EqualityMatchingRuleCfgDefn definition =
596 EqualityMatchingRuleCfgDefn.getInstance();
597 ClassPropertyDefinition propertyDefinition =
598 definition.getJavaClassPropertyDefinition();
599 Class<? extends EqualityMatchingRule> equalityMatchingRuleClass =
600 propertyDefinition.loadClass(className,
601 EqualityMatchingRule.class);
602 matchingRule = equalityMatchingRuleClass.newInstance();
603 }
604 else if (configuration instanceof OrderingMatchingRuleCfg)
605 {
606 OrderingMatchingRuleCfgDefn definition =
607 OrderingMatchingRuleCfgDefn.getInstance();
608 ClassPropertyDefinition propertyDefinition =
609 definition.getJavaClassPropertyDefinition();
610 Class<? extends OrderingMatchingRule> orderingMatchingRuleClass =
611 propertyDefinition.loadClass(className,
612 OrderingMatchingRule.class);
613 matchingRule = orderingMatchingRuleClass.newInstance();
614 }
615 else if (configuration instanceof SubstringMatchingRuleCfg)
616 {
617 SubstringMatchingRuleCfgDefn definition =
618 SubstringMatchingRuleCfgDefn.getInstance();
619 ClassPropertyDefinition propertyDefinition =
620 definition.getJavaClassPropertyDefinition();
621 Class<? extends SubstringMatchingRule> substringMatchingRuleClass =
622 propertyDefinition.loadClass(className,
623 SubstringMatchingRule.class);
624 matchingRule = substringMatchingRuleClass.newInstance();
625 }
626 else
627 {
628 throw new AssertionError("Unsupported matching rule type: " +
629 className + " with config type " +
630 configuration.getClass().getName());
631 }
632
633 if (initialize)
634 {
635 Method method = matchingRule.getClass().getMethod(
636 "initializeMatchingRule", configuration.configurationClass());
637 method.invoke(matchingRule, configuration);
638 }
639 else
640 {
641 Method method =
642 matchingRule.getClass().getMethod("isConfigurationAcceptable",
643 MatchingRuleCfg.class,
644 List.class);
645
646 List<Message> unacceptableReasons = new ArrayList<Message>();
647 Boolean acceptable = (Boolean) method.invoke(matchingRule,
648 configuration,
649 unacceptableReasons);
650 if (! acceptable)
651 {
652 StringBuilder buffer = new StringBuilder();
653 if (! unacceptableReasons.isEmpty())
654 {
655 Iterator<Message> iterator = unacceptableReasons.iterator();
656 buffer.append(iterator.next());
657 while (iterator.hasNext())
658 {
659 buffer.append(". ");
660 buffer.append(iterator.next());
661 }
662 }
663
664 Message message = ERR_CONFIG_SCHEMA_MR_CONFIG_NOT_ACCEPTABLE.get(
665 String.valueOf(configuration.dn()), buffer.toString());
666 throw new InitializationException(message);
667 }
668 }
669
670 return matchingRule;
671 }
672 catch (Exception e)
673 {
674 Message message = ERR_CONFIG_SCHEMA_MR_CANNOT_INITIALIZE.
675 get(className, String.valueOf(configuration.dn()),
676 stackTraceToSingleLineString(e));
677 throw new InitializationException(message, e);
678 }
679 }
680 }
681