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