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
029
030
031 import java.lang.reflect.Method;
032 import java.util.ArrayList;
033 import java.util.Iterator;
034 import java.util.List;
035 import java.util.concurrent.ConcurrentHashMap;
036
037 import org.opends.server.admin.ClassPropertyDefinition;
038 import org.opends.server.admin.server.ConfigurationAddListener;
039 import org.opends.server.admin.server.ConfigurationChangeListener;
040 import org.opends.server.admin.server.ConfigurationDeleteListener;
041 import org.opends.server.admin.std.meta.AttributeSyntaxCfgDefn;
042 import org.opends.server.admin.std.server.AttributeSyntaxCfg;
043 import org.opends.server.admin.std.server.RootCfg;
044 import org.opends.server.admin.server.ServerManagementContext;
045 import org.opends.server.api.AttributeSyntax;
046 import org.opends.server.config.ConfigException;
047 import org.opends.messages.Message;
048 import org.opends.server.types.AttributeType;
049 import org.opends.server.types.ConfigChangeResult;
050 import org.opends.server.types.DirectoryException;
051 import org.opends.server.types.DN;
052
053
054 import org.opends.server.types.InitializationException;
055 import org.opends.server.types.ResultCode;
056
057 import static org.opends.server.loggers.ErrorLogger.*;
058 import static org.opends.messages.ConfigMessages.*;
059 import static org.opends.server.util.StaticUtils.*;
060
061
062
063 /**
064 * This class defines a utility that will be used to manage the set of attribute
065 * syntaxes defined in the Directory Server. It wil initialize the syntaxes
066 * when the server starts, and then will manage any additions, removals, or
067 * modifications to any syntaxes while the server is running.
068 */
069 public class AttributeSyntaxConfigManager
070 implements ConfigurationChangeListener<AttributeSyntaxCfg>,
071 ConfigurationAddListener<AttributeSyntaxCfg>,
072 ConfigurationDeleteListener<AttributeSyntaxCfg>
073
074 {
075 // A mapping between the DNs of the config entries and the associated
076 // attribute syntaxes.
077 private ConcurrentHashMap<DN,AttributeSyntax> syntaxes;
078
079
080
081 /**
082 * Creates a new instance of this attribute syntax config manager.
083 */
084 public AttributeSyntaxConfigManager()
085 {
086 syntaxes = new ConcurrentHashMap<DN,AttributeSyntax>();
087 }
088
089
090
091 /**
092 * Initializes all attribute syntaxes currently defined in the Directory
093 * Server configuration. This should only be called at Directory Server
094 * startup.
095 *
096 * @throws ConfigException If a configuration problem causes the attribute
097 * syntax initialization process to fail.
098 *
099 * @throws InitializationException If a problem occurs while initializing
100 * the attribute syntaxes that is not
101 * related to the server configuration.
102 */
103 public void initializeAttributeSyntaxes()
104 throws ConfigException, InitializationException
105 {
106 // Get the root configuration object.
107 ServerManagementContext managementContext =
108 ServerManagementContext.getInstance();
109 RootCfg rootConfiguration =
110 managementContext.getRootConfiguration();
111
112
113 // Register as an add and delete listener with the root configuration so we
114 // can be notified if any attribute syntax entries are added or removed.
115 rootConfiguration.addAttributeSyntaxAddListener(this);
116 rootConfiguration.addAttributeSyntaxDeleteListener(this);
117
118
119 //Initialize the existing attribute syntaxes.
120 for (String name : rootConfiguration.listAttributeSyntaxes())
121 {
122 AttributeSyntaxCfg syntaxConfiguration =
123 rootConfiguration.getAttributeSyntax(name);
124 syntaxConfiguration.addChangeListener(this);
125
126 if (syntaxConfiguration.isEnabled())
127 {
128 String className = syntaxConfiguration.getJavaClass();
129 try
130 {
131 AttributeSyntax syntax = loadSyntax(className, syntaxConfiguration,
132 true);
133
134 try
135 {
136 DirectoryServer.registerAttributeSyntax(syntax, false);
137 syntaxes.put(syntaxConfiguration.dn(), syntax);
138 }
139 catch (DirectoryException de)
140 {
141 Message message = WARN_CONFIG_SCHEMA_SYNTAX_CONFLICTING_SYNTAX.get(
142 String.valueOf(syntaxConfiguration.dn()), de.getMessageObject());
143 logError(message);
144 continue;
145 }
146 }
147 catch (InitializationException ie)
148 {
149 logError(ie.getMessageObject());
150 continue;
151 }
152 }
153 }
154 }
155
156
157
158 /**
159 * {@inheritDoc}
160 */
161 public boolean isConfigurationAddAcceptable(
162 AttributeSyntaxCfg configuration,
163 List<Message> unacceptableReasons)
164 {
165 if (configuration.isEnabled())
166 {
167 // Get the name of the class and make sure we can instantiate it as an
168 // attribute syntax.
169 String className = configuration.getJavaClass();
170 try
171 {
172 loadSyntax(className, configuration, false);
173 }
174 catch (InitializationException ie)
175 {
176 unacceptableReasons.add(ie.getMessageObject());
177 return false;
178 }
179 }
180
181 // If we've gotten here, then it's fine.
182 return true;
183 }
184
185
186
187 /**
188 * {@inheritDoc}
189 */
190 public ConfigChangeResult applyConfigurationAdd(
191 AttributeSyntaxCfg configuration)
192 {
193 ResultCode resultCode = ResultCode.SUCCESS;
194 boolean adminActionRequired = false;
195 ArrayList<Message> messages = new ArrayList<Message>();
196
197 configuration.addChangeListener(this);
198
199 if (! configuration.isEnabled())
200 {
201 return new ConfigChangeResult(resultCode, adminActionRequired, messages);
202 }
203
204 AttributeSyntax syntax = null;
205
206 // Get the name of the class and make sure we can instantiate it as an
207 // attribute syntax.
208 String className = configuration.getJavaClass();
209 try
210 {
211 syntax = loadSyntax(className, configuration, true);
212
213 try
214 {
215 DirectoryServer.registerAttributeSyntax(syntax, false);
216 syntaxes.put(configuration.dn(), syntax);
217 }
218 catch (DirectoryException de)
219 {
220 Message message = WARN_CONFIG_SCHEMA_SYNTAX_CONFLICTING_SYNTAX.get(
221 String.valueOf(configuration.dn()), de.getMessageObject());
222 messages.add(message);
223
224 if (resultCode == ResultCode.SUCCESS)
225 {
226 resultCode = DirectoryServer.getServerErrorResultCode();
227 }
228 }
229 }
230 catch (InitializationException ie)
231 {
232 if (resultCode == ResultCode.SUCCESS)
233 {
234 resultCode = DirectoryServer.getServerErrorResultCode();
235 }
236
237 messages.add(ie.getMessageObject());
238 }
239
240 return new ConfigChangeResult(resultCode, adminActionRequired, messages);
241 }
242
243
244
245 /**
246 * {@inheritDoc}
247 */
248 public boolean isConfigurationDeleteAcceptable(
249 AttributeSyntaxCfg configuration,
250 List<Message> unacceptableReasons)
251 {
252 // If the syntax is enabled, then check to see if there are any defined
253 // attribute types that use the syntax. If so, then don't allow it to be
254 // deleted.
255 boolean configAcceptable = true;
256 AttributeSyntax syntax = syntaxes.get(configuration.dn());
257 if (syntax != null)
258 {
259 String oid = syntax.getOID();
260 for (AttributeType at : DirectoryServer.getAttributeTypes().values())
261 {
262 if (oid.equals(at.getSyntaxOID()))
263 {
264 Message message = WARN_CONFIG_SCHEMA_CANNOT_DELETE_SYNTAX_IN_USE.get(
265 syntax.getSyntaxName(), at.getNameOrOID());
266 unacceptableReasons.add(message);
267
268 configAcceptable = false;
269 }
270 }
271 }
272
273 return configAcceptable;
274 }
275
276
277
278 /**
279 * {@inheritDoc}
280 */
281 public ConfigChangeResult applyConfigurationDelete(
282 AttributeSyntaxCfg configuration)
283 {
284 ResultCode resultCode = ResultCode.SUCCESS;
285 boolean adminActionRequired = false;
286 ArrayList<Message> messages = new ArrayList<Message>();
287
288 AttributeSyntax syntax = syntaxes.remove(configuration.dn());
289 if (syntax != null)
290 {
291 DirectoryServer.deregisterAttributeSyntax(syntax);
292 syntax.finalizeSyntax();
293 }
294
295 return new ConfigChangeResult(resultCode, adminActionRequired, messages);
296 }
297
298
299
300 /**
301 * {@inheritDoc}
302 */
303 public boolean isConfigurationChangeAcceptable(
304 AttributeSyntaxCfg configuration,
305 List<Message> unacceptableReasons)
306 {
307 if (configuration.isEnabled())
308 {
309 // Get the name of the class and make sure we can instantiate it as an
310 // attribute syntax.
311 String className = configuration.getJavaClass();
312 try
313 {
314 loadSyntax(className, configuration, false);
315 }
316 catch (InitializationException ie)
317 {
318 unacceptableReasons.add(ie.getMessageObject());
319 return false;
320 }
321 }
322 else
323 {
324 // If the syntax is currently enabled and the change would make it
325 // disabled, then only allow it if the syntax isn't already in use.
326 AttributeSyntax syntax = syntaxes.get(configuration.dn());
327 if (syntax != null)
328 {
329 String oid = syntax.getOID();
330 for (AttributeType at : DirectoryServer.getAttributeTypes().values())
331 {
332 if (oid.equals(at.getSyntaxOID()))
333 {
334 Message message =
335 WARN_CONFIG_SCHEMA_CANNOT_DISABLE_SYNTAX_IN_USE.get(
336 syntax.getSyntaxName(),
337 at.getNameOrOID());
338 unacceptableReasons.add(message);
339 return false;
340 }
341 }
342 }
343 }
344
345 // If we've gotten here, then it's fine.
346 return true;
347 }
348
349
350
351 /**
352 * {@inheritDoc}
353 */
354 public ConfigChangeResult applyConfigurationChange(
355 AttributeSyntaxCfg configuration)
356 {
357 ResultCode resultCode = ResultCode.SUCCESS;
358 boolean adminActionRequired = false;
359 ArrayList<Message> messages = new ArrayList<Message>();
360
361
362 // Get the existing syntax if it's already enabled.
363 AttributeSyntax existingSyntax = syntaxes.get(configuration.dn());
364
365
366 // If the new configuration has the syntax disabled, then disable it if it
367 // is enabled, or do nothing if it's already disabled.
368 if (! configuration.isEnabled())
369 {
370 if (existingSyntax != null)
371 {
372 DirectoryServer.deregisterAttributeSyntax(existingSyntax);
373
374 AttributeSyntax syntax = syntaxes.remove(configuration.dn());
375 if (syntax != null)
376 {
377 syntax.finalizeSyntax();
378 }
379 }
380
381 return new ConfigChangeResult(resultCode, adminActionRequired, messages);
382 }
383
384
385 // Get the class for the attribute syntax. If the syntax is already
386 // enabled, then we shouldn't do anything with it although if the class has
387 // changed then we'll at least need to indicate that administrative action
388 // is required. If the syntax is disabled, then instantiate the class and
389 // initialize and register it as an attribute syntax.
390 String className = configuration.getJavaClass();
391 if (existingSyntax != null)
392 {
393 if (! className.equals(existingSyntax.getClass().getName()))
394 {
395 adminActionRequired = true;
396 }
397
398 return new ConfigChangeResult(resultCode, adminActionRequired, messages);
399 }
400
401 AttributeSyntax syntax = null;
402 try
403 {
404 syntax = loadSyntax(className, configuration, true);
405
406 try
407 {
408 DirectoryServer.registerAttributeSyntax(syntax, false);
409 syntaxes.put(configuration.dn(), syntax);
410 }
411 catch (DirectoryException de)
412 {
413 Message message = WARN_CONFIG_SCHEMA_SYNTAX_CONFLICTING_SYNTAX.get(
414 String.valueOf(configuration.dn()),
415 de.getMessageObject());
416 messages.add(message);
417
418 if (resultCode == ResultCode.SUCCESS)
419 {
420 resultCode = DirectoryServer.getServerErrorResultCode();
421 }
422 }
423 }
424 catch (InitializationException ie)
425 {
426 if (resultCode == ResultCode.SUCCESS)
427 {
428 resultCode = DirectoryServer.getServerErrorResultCode();
429 }
430
431 messages.add(ie.getMessageObject());
432 }
433
434 return new ConfigChangeResult(resultCode, adminActionRequired, messages);
435 }
436
437
438
439 /**
440 * Loads the specified class, instantiates it as an attribute syntax, and
441 * optionally initializes that instance.
442 *
443 * @param className The fully-qualified name of the attribute syntax
444 * class to load, instantiate, and initialize.
445 * @param configuration The configuration to use to initialize the attribute
446 * syntax. It should not be {@code null}.
447 * @param initialize Indicates whether the attribute syntax instance
448 * should be initialized.
449 *
450 * @return The possibly initialized attribute syntax.
451 *
452 * @throws InitializationException If a problem occurred while attempting to
453 * initialize the attribute syntax.
454 */
455 private AttributeSyntax loadSyntax(String className,
456 AttributeSyntaxCfg configuration,
457 boolean initialize)
458 throws InitializationException
459 {
460 try
461 {
462 AttributeSyntaxCfgDefn definition =
463 AttributeSyntaxCfgDefn.getInstance();
464 ClassPropertyDefinition propertyDefinition =
465 definition.getJavaClassPropertyDefinition();
466 Class<? extends AttributeSyntax> syntaxClass =
467 propertyDefinition.loadClass(className, AttributeSyntax.class);
468 AttributeSyntax syntax = syntaxClass.newInstance();
469
470 if (initialize)
471 {
472 Method method = syntax.getClass().getMethod("initializeSyntax",
473 configuration.configurationClass());
474 method.invoke(syntax, configuration);
475 }
476 else
477 {
478 Method method = syntax.getClass().getMethod("isConfigurationAcceptable",
479 AttributeSyntaxCfg.class,
480 List.class);
481
482 List<Message> unacceptableReasons = new ArrayList<Message>();
483 Boolean acceptable = (Boolean) method.invoke(syntax, configuration,
484 unacceptableReasons);
485 if (! acceptable)
486 {
487 StringBuilder buffer = new StringBuilder();
488 if (! unacceptableReasons.isEmpty())
489 {
490 Iterator<Message> iterator = unacceptableReasons.iterator();
491 buffer.append(iterator.next());
492 while (iterator.hasNext())
493 {
494 buffer.append(". ");
495 buffer.append(iterator.next());
496 }
497 }
498
499 Message message = ERR_CONFIG_SCHEMA_SYNTAX_CONFIG_NOT_ACCEPTABLE.get(
500 String.valueOf(configuration.dn()), buffer.toString());
501 throw new InitializationException(message);
502 }
503 }
504
505 return syntax;
506 }
507 catch (Exception e)
508 {
509 Message message = ERR_CONFIG_SCHEMA_SYNTAX_CANNOT_INITIALIZE.
510 get(className, String.valueOf(configuration.dn()),
511 stackTraceToSingleLineString(e));
512 throw new InitializationException(message, e);
513 }
514 }
515 }
516