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 2007-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.LinkedHashSet;
037 import java.util.concurrent.ConcurrentHashMap;
038
039 import org.opends.server.admin.ClassPropertyDefinition;
040 import org.opends.server.admin.server.ConfigurationAddListener;
041 import org.opends.server.admin.server.ConfigurationChangeListener;
042 import org.opends.server.admin.server.ConfigurationDeleteListener;
043 import org.opends.server.admin.std.meta.VirtualAttributeCfgDefn;
044 import org.opends.server.admin.std.server.VirtualAttributeCfg;
045 import org.opends.server.admin.std.server.RootCfg;
046 import org.opends.server.admin.server.ServerManagementContext;
047 import org.opends.server.api.VirtualAttributeProvider;
048 import org.opends.server.config.ConfigException;
049 import org.opends.server.types.ConfigChangeResult;
050 import org.opends.server.types.DebugLogLevel;
051 import org.opends.server.types.DirectoryException;
052 import org.opends.server.types.DN;
053
054
055 import org.opends.server.types.InitializationException;
056 import org.opends.server.types.ResultCode;
057 import org.opends.server.types.SearchFilter;
058 import org.opends.server.types.VirtualAttributeRule;
059
060 import static org.opends.server.loggers.debug.DebugLogger.*;
061 import org.opends.server.loggers.debug.DebugTracer;
062 import org.opends.server.loggers.ErrorLogger;
063 import static org.opends.messages.ConfigMessages.*;
064
065 import static org.opends.server.util.StaticUtils.*;
066
067
068
069 /**
070 * This class defines a utility that will be used to manage the set of
071 * virtual attribute providers defined in the Directory Server. It will
072 * initialize the providers when the server starts, and then will manage any
073 * additions, removals, or modifications to any virtual attribute providers
074 * while the server is running.
075 */
076 public class VirtualAttributeConfigManager
077 implements ConfigurationChangeListener<VirtualAttributeCfg>,
078 ConfigurationAddListener<VirtualAttributeCfg>,
079 ConfigurationDeleteListener<VirtualAttributeCfg>
080 {
081 /**
082 * The tracer object for the debug logger.
083 */
084 private static final DebugTracer TRACER = getTracer();
085
086 // A mapping between the DNs of the config entries and the associated
087 // virtual attribute rules.
088 private ConcurrentHashMap<DN,VirtualAttributeRule> rules;
089
090
091
092 /**
093 * Creates a new instance of this virtual attribute config manager.
094 */
095 public VirtualAttributeConfigManager()
096 {
097 rules = new ConcurrentHashMap<DN,VirtualAttributeRule>();
098 }
099
100
101
102 /**
103 * Initializes all virtual attribute providers currently defined in the
104 * Directory Server configuration. This should only be called at Directory
105 * Server startup.
106 *
107 * @throws ConfigException If a configuration problem causes the virtual
108 * attribute provider initialization process to
109 * fail.
110 *
111 * @throws InitializationException If a problem occurs while initializing
112 * the virtual attribute providers that is
113 * not related to the server configuration.
114 */
115 public void initializeVirtualAttributes()
116 throws ConfigException, InitializationException
117 {
118 // Get the root configuration object.
119 ServerManagementContext managementContext =
120 ServerManagementContext.getInstance();
121 RootCfg rootConfiguration =
122 managementContext.getRootConfiguration();
123
124
125 // Register as an add and delete listener with the root configuration so we
126 // can be notified if any virtual attribute provider entries are added or
127 // removed.
128 rootConfiguration.addVirtualAttributeAddListener(this);
129 rootConfiguration.addVirtualAttributeDeleteListener(this);
130
131
132 //Initialize the existing virtual attribute providers.
133 for (String providerName : rootConfiguration.listVirtualAttributes())
134 {
135 VirtualAttributeCfg cfg =
136 rootConfiguration.getVirtualAttribute(providerName);
137 cfg.addChangeListener(this);
138
139 if (cfg.isEnabled())
140 {
141 String className = cfg.getJavaClass();
142 try
143 {
144 VirtualAttributeProvider<? extends VirtualAttributeCfg> provider =
145 loadProvider(className, cfg, true);
146
147 LinkedHashSet<SearchFilter> filters =
148 new LinkedHashSet<SearchFilter>();
149 for (String filterString : cfg.getFilter())
150 {
151 try
152 {
153 filters.add(SearchFilter.createFilterFromString(filterString));
154 }
155 catch (DirectoryException de)
156 {
157 if (debugEnabled())
158 {
159 TRACER.debugCaught(DebugLogLevel.ERROR, de);
160 }
161
162 Message message = ERR_CONFIG_VATTR_INVALID_SEARCH_FILTER.get(
163 filterString, String.valueOf(cfg.dn()),
164 de.getMessageObject());
165 throw new ConfigException(message, de);
166 }
167 }
168
169 if (cfg.getAttributeType().isSingleValue())
170 {
171 if (provider.isMultiValued())
172 {
173 Message message = ERR_CONFIG_VATTR_SV_TYPE_WITH_MV_PROVIDER.
174 get(String.valueOf(cfg.dn()),
175 cfg.getAttributeType().getNameOrOID(), className);
176 throw new ConfigException(message);
177 }
178 else if (cfg.getConflictBehavior() ==
179 VirtualAttributeCfgDefn.ConflictBehavior.
180 MERGE_REAL_AND_VIRTUAL)
181 {
182 Message message = ERR_CONFIG_VATTR_SV_TYPE_WITH_MERGE_VALUES.
183 get(String.valueOf(cfg.dn()),
184 cfg.getAttributeType().getNameOrOID());
185 throw new ConfigException(message);
186 }
187 }
188
189 VirtualAttributeRule rule =
190 new VirtualAttributeRule(cfg.getAttributeType(), provider,
191 cfg.getBaseDN(), cfg.getGroupDN(),
192 filters, cfg.getConflictBehavior());
193 rules.put(cfg.dn(), rule);
194 DirectoryServer.registerVirtualAttribute(rule);
195 }
196 catch (InitializationException ie)
197 {
198 ErrorLogger.logError(ie.getMessageObject());
199 continue;
200 }
201 }
202 }
203 }
204
205
206
207 /**
208 * {@inheritDoc}
209 */
210 public boolean isConfigurationAddAcceptable(
211 VirtualAttributeCfg configuration,
212 List<Message> unacceptableReasons)
213 {
214 if (configuration.isEnabled())
215 {
216 // Get the name of the class and make sure we can instantiate it as a
217 // virtual attribute provider.
218 String className = configuration.getJavaClass();
219 try
220 {
221 loadProvider(className, configuration, false);
222 }
223 catch (InitializationException ie)
224 {
225 unacceptableReasons.add(ie.getMessageObject());
226 return false;
227 }
228 }
229
230 // If there were any search filters provided, then make sure they are all
231 // valid.
232 for (String filterString : configuration.getFilter())
233 {
234 try
235 {
236 SearchFilter.createFilterFromString(filterString);
237 }
238 catch (DirectoryException de)
239 {
240 if (debugEnabled())
241 {
242 TRACER.debugCaught(DebugLogLevel.ERROR, de);
243 }
244
245 Message message = ERR_CONFIG_VATTR_INVALID_SEARCH_FILTER.get(
246 filterString,
247 String.valueOf(configuration.dn()),
248 de.getMessageObject());
249 unacceptableReasons.add(message);
250 return false;
251 }
252 }
253
254 // If we've gotten here, then it's fine.
255 return true;
256 }
257
258
259
260 /**
261 * {@inheritDoc}
262 */
263 public ConfigChangeResult applyConfigurationAdd(
264 VirtualAttributeCfg configuration)
265 {
266 ResultCode resultCode = ResultCode.SUCCESS;
267 boolean adminActionRequired = false;
268 ArrayList<Message> messages = new ArrayList<Message>();
269
270 configuration.addChangeListener(this);
271
272 if (! configuration.isEnabled())
273 {
274 return new ConfigChangeResult(resultCode, adminActionRequired, messages);
275 }
276
277 // Make sure that we can parse all of the search filters.
278 LinkedHashSet<SearchFilter> filters =
279 new LinkedHashSet<SearchFilter>();
280 for (String filterString : configuration.getFilter())
281 {
282 try
283 {
284 filters.add(SearchFilter.createFilterFromString(filterString));
285 }
286 catch (DirectoryException de)
287 {
288 if (debugEnabled())
289 {
290 TRACER.debugCaught(DebugLogLevel.ERROR, de);
291 }
292
293 if (resultCode == ResultCode.SUCCESS)
294 {
295 resultCode = ResultCode.INVALID_ATTRIBUTE_SYNTAX;
296 }
297
298 Message message = ERR_CONFIG_VATTR_INVALID_SEARCH_FILTER.get(
299 filterString,
300 String.valueOf(configuration.dn()),
301 de.getMessageObject());
302 messages.add(message);
303 }
304 }
305
306 // Get the name of the class and make sure we can instantiate it as a
307 // certificate mapper.
308 VirtualAttributeProvider<? extends VirtualAttributeCfg> provider = null;
309 if (resultCode == ResultCode.SUCCESS)
310 {
311 String className = configuration.getJavaClass();
312 try
313 {
314 provider = loadProvider(className, configuration, true);
315 }
316 catch (InitializationException ie)
317 {
318 resultCode = DirectoryServer.getServerErrorResultCode();
319 messages.add(ie.getMessageObject());
320 }
321 }
322
323 if (resultCode == ResultCode.SUCCESS)
324 {
325 VirtualAttributeRule rule =
326 new VirtualAttributeRule(configuration.getAttributeType(), provider,
327 configuration.getBaseDN(),
328 configuration.getGroupDN(),
329 filters,
330 configuration.getConflictBehavior());
331
332 rules.put(configuration.dn(), rule);
333 DirectoryServer.registerVirtualAttribute(rule);
334 }
335
336 return new ConfigChangeResult(resultCode, adminActionRequired, messages);
337 }
338
339
340
341 /**
342 * {@inheritDoc}
343 */
344 public boolean isConfigurationDeleteAcceptable(
345 VirtualAttributeCfg configuration,
346 List<Message> unacceptableReasons)
347 {
348 // We will always allow getting rid of a virtual attribute rule.
349 return true;
350 }
351
352
353
354 /**
355 * {@inheritDoc}
356 */
357 public ConfigChangeResult applyConfigurationDelete(
358 VirtualAttributeCfg configuration)
359 {
360 ResultCode resultCode = ResultCode.SUCCESS;
361 boolean adminActionRequired = false;
362 ArrayList<Message> messages = new ArrayList<Message>();
363
364 VirtualAttributeRule rule = rules.remove(configuration.dn());
365 if (rule != null)
366 {
367 DirectoryServer.deregisterVirtualAttribute(rule);
368 rule.getProvider().finalizeVirtualAttributeProvider();
369 }
370
371 return new ConfigChangeResult(resultCode, adminActionRequired, messages);
372 }
373
374
375
376 /**
377 * {@inheritDoc}
378 */
379 public boolean isConfigurationChangeAcceptable(
380 VirtualAttributeCfg configuration,
381 List<Message> unacceptableReasons)
382 {
383 if (configuration.isEnabled())
384 {
385 // Get the name of the class and make sure we can instantiate it as a
386 // virtual attribute provider.
387 String className = configuration.getJavaClass();
388 try
389 {
390 loadProvider(className, configuration, false);
391 }
392 catch (InitializationException ie)
393 {
394 unacceptableReasons.add(ie.getMessageObject());
395 return false;
396 }
397 }
398
399 // If there were any search filters provided, then make sure they are all
400 // valid.
401 for (String filterString : configuration.getFilter())
402 {
403 try
404 {
405 SearchFilter.createFilterFromString(filterString);
406 }
407 catch (DirectoryException de)
408 {
409 if (debugEnabled())
410 {
411 TRACER.debugCaught(DebugLogLevel.ERROR, de);
412 }
413
414 Message message = ERR_CONFIG_VATTR_INVALID_SEARCH_FILTER.get(
415 filterString,
416 String.valueOf(configuration.dn()),
417 de.getMessageObject());
418 unacceptableReasons.add(message);
419 return false;
420 }
421 }
422
423 // If we've gotten here, then it's fine.
424 return true;
425 }
426
427
428
429 /**
430 * {@inheritDoc}
431 */
432 public ConfigChangeResult applyConfigurationChange(
433 VirtualAttributeCfg configuration)
434 {
435 ResultCode resultCode = ResultCode.SUCCESS;
436 boolean adminActionRequired = false;
437 ArrayList<Message> messages = new ArrayList<Message>();
438
439
440 // Get the existing rule if it's already enabled.
441 VirtualAttributeRule existingRule = rules.get(configuration.dn());
442
443
444 // If the new configuration has the rule disabled, then disable it if it
445 // is enabled, or do nothing if it's already disabled.
446 if (! configuration.isEnabled())
447 {
448 if (existingRule != null)
449 {
450 DirectoryServer.deregisterVirtualAttribute(existingRule);
451 existingRule.getProvider().finalizeVirtualAttributeProvider();
452 }
453
454 return new ConfigChangeResult(resultCode, adminActionRequired, messages);
455 }
456
457
458 // Make sure that we can parse all of the search filters.
459 LinkedHashSet<SearchFilter> filters =
460 new LinkedHashSet<SearchFilter>();
461 for (String filterString : configuration.getFilter())
462 {
463 try
464 {
465 filters.add(SearchFilter.createFilterFromString(filterString));
466 }
467 catch (DirectoryException de)
468 {
469 if (debugEnabled())
470 {
471 TRACER.debugCaught(DebugLogLevel.ERROR, de);
472 }
473
474 if (resultCode == ResultCode.SUCCESS)
475 {
476 resultCode = ResultCode.INVALID_ATTRIBUTE_SYNTAX;
477 }
478
479 Message message = ERR_CONFIG_VATTR_INVALID_SEARCH_FILTER.get(
480 filterString,
481 String.valueOf(configuration.dn()),
482 de.getMessageObject());
483 messages.add(message);
484 }
485 }
486
487 // Get the name of the class and make sure we can instantiate it as a
488 // certificate mapper.
489 VirtualAttributeProvider<? extends VirtualAttributeCfg> provider = null;
490 if (resultCode == ResultCode.SUCCESS)
491 {
492 String className = configuration.getJavaClass();
493 try
494 {
495 provider = loadProvider(className, configuration, true);
496 }
497 catch (InitializationException ie)
498 {
499 resultCode = DirectoryServer.getServerErrorResultCode();
500 messages.add(ie.getMessageObject());
501 }
502 }
503
504 if (resultCode == ResultCode.SUCCESS)
505 {
506 VirtualAttributeRule rule =
507 new VirtualAttributeRule(configuration.getAttributeType(), provider,
508 configuration.getBaseDN(),
509 configuration.getGroupDN(),
510 filters,
511 configuration.getConflictBehavior());
512
513 rules.put(configuration.dn(), rule);
514 if (existingRule == null)
515 {
516 DirectoryServer.registerVirtualAttribute(rule);
517 }
518 else
519 {
520 DirectoryServer.replaceVirtualAttribute(existingRule, rule);
521 existingRule.getProvider().finalizeVirtualAttributeProvider();
522 }
523 }
524
525 return new ConfigChangeResult(resultCode, adminActionRequired, messages);
526 }
527
528
529
530 /**
531 * Loads the specified class, instantiates it as a certificate mapper, and
532 * optionally initializes that instance.
533 *
534 * @param className The fully-qualified name of the certificate mapper
535 * class to load, instantiate, and initialize.
536 * @param configuration The configuration to use to initialize the
537 * virtual attribute provider. It must not be
538 * {@code null}.
539 * @param initialize Indicates whether the virtual attribute provider
540 * instance should be initialized.
541 *
542 * @return The possibly initialized certificate mapper.
543 *
544 * @throws InitializationException If a problem occurred while attempting to
545 * initialize the certificate mapper.
546 */
547 private VirtualAttributeProvider<? extends VirtualAttributeCfg>
548 loadProvider(String className, VirtualAttributeCfg configuration,
549 boolean initialize)
550 throws InitializationException
551 {
552 try
553 {
554 VirtualAttributeCfgDefn definition =
555 VirtualAttributeCfgDefn.getInstance();
556 ClassPropertyDefinition propertyDefinition =
557 definition.getJavaClassPropertyDefinition();
558 Class<? extends VirtualAttributeProvider> providerClass =
559 propertyDefinition.loadClass(className,
560 VirtualAttributeProvider.class);
561 VirtualAttributeProvider<? extends VirtualAttributeCfg> provider =
562 (VirtualAttributeProvider<? extends VirtualAttributeCfg>)
563 providerClass.newInstance();
564
565 if (initialize)
566 {
567 Method method = provider.getClass().getMethod(
568 "initializeVirtualAttributeProvider",
569 configuration.configurationClass());
570 method.invoke(provider, configuration);
571 }
572 else
573 {
574 Method method =
575 provider.getClass().getMethod("isConfigurationAcceptable",
576 VirtualAttributeCfg.class,
577 List.class);
578
579 List<Message> unacceptableReasons = new ArrayList<Message>();
580 Boolean acceptable = (Boolean) method.invoke(provider, configuration,
581 unacceptableReasons);
582 if (! acceptable)
583 {
584 StringBuilder buffer = new StringBuilder();
585 if (! unacceptableReasons.isEmpty())
586 {
587 Iterator<Message> iterator = unacceptableReasons.iterator();
588 buffer.append(iterator.next());
589 while (iterator.hasNext())
590 {
591 buffer.append(". ");
592 buffer.append(iterator.next());
593 }
594 }
595
596 Message message = ERR_CONFIG_VATTR_CONFIG_NOT_ACCEPTABLE.get(
597 String.valueOf(configuration.dn()), buffer.toString());
598 throw new InitializationException(message);
599 }
600 }
601
602 return provider;
603 }
604 catch (Exception e)
605 {
606 Message message = ERR_CONFIG_VATTR_INITIALIZATION_FAILED.
607 get(className, String.valueOf(configuration.dn()),
608 stackTraceToSingleLineString(e));
609 throw new InitializationException(message, e);
610 }
611 }
612 }
613