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.util.ArrayList;
033 import java.util.List;
034 import java.util.concurrent.ConcurrentHashMap;
035 import java.lang.reflect.Method;
036
037 import org.opends.server.admin.ClassPropertyDefinition;
038 import org.opends.server.admin.server.ConfigurationChangeListener;
039 import org.opends.server.admin.server.ConfigurationAddListener;
040 import org.opends.server.admin.server.ConfigurationDeleteListener;
041 import org.opends.server.admin.server.ServerManagementContext;
042 import org.opends.server.admin.std.server.ExtendedOperationHandlerCfg;
043 import org.opends.server.admin.std.server.RootCfg;
044 import org.opends.server.admin.std.meta.ExtendedOperationHandlerCfgDefn;
045 import org.opends.server.api.ExtendedOperationHandler;
046 import org.opends.server.config.ConfigException;
047 import org.opends.server.loggers.debug.DebugTracer;
048 import org.opends.server.types.ConfigChangeResult;
049 import org.opends.server.types.DebugLogLevel;
050 import org.opends.server.types.DN;
051 import org.opends.server.types.InitializationException;
052 import org.opends.server.types.ResultCode;
053
054 import static org.opends.server.loggers.debug.DebugLogger.*;
055 import static org.opends.messages.ConfigMessages.*;
056
057 import static org.opends.server.util.StaticUtils.stackTraceToSingleLineString;
058
059
060
061 /**
062 * This class defines a utility that will be used to manage the set of extended
063 * operation handlers defined in the Directory Server. It will initialize the
064 * handlers when the server starts, and then will manage any additions,
065 * removals, or modifications of any extended operation handlers while the
066 * server is running.
067 */
068 public class ExtendedOperationConfigManager implements
069 ConfigurationChangeListener<ExtendedOperationHandlerCfg>,
070 ConfigurationAddListener<ExtendedOperationHandlerCfg>,
071 ConfigurationDeleteListener<ExtendedOperationHandlerCfg>
072 {
073 /**
074 * The tracer object for the debug logger.
075 */
076 private static final DebugTracer TRACER = getTracer();
077
078
079
080 // A mapping between the DNs of the config entries and the associated extended
081 // operation handlers.
082 private ConcurrentHashMap<DN,ExtendedOperationHandler> handlers;
083
084
085
086 /**
087 * Creates a new instance of this extended operation config manager.
088 */
089 public ExtendedOperationConfigManager()
090 {
091 handlers = new ConcurrentHashMap<DN,ExtendedOperationHandler>();
092 }
093
094
095
096 /**
097 * Initializes all extended operation handlers currently defined in the
098 * Directory Server configuration. This should only be called at Directory
099 * Server startup.
100 *
101 * @throws ConfigException If a configuration problem causes the extended
102 * operation handler initialization process to fail.
103 *
104 * @throws InitializationException If a problem occurs while initializing
105 * the extended operation handler that is
106 * not related to the server configuration.
107 */
108 public void initializeExtendedOperationHandlers()
109 throws ConfigException, InitializationException
110 {
111 // Create an internal server management context and retrieve
112 // the root configuration which has the extended operation handler relation.
113 ServerManagementContext context = ServerManagementContext.getInstance();
114 RootCfg root = context.getRootConfiguration();
115
116 // Register add and delete listeners.
117 root.addExtendedOperationHandlerAddListener(this);
118 root.addExtendedOperationHandlerDeleteListener(this);
119
120 // Initialize existing handlers.
121 for (String name : root.listExtendedOperationHandlers())
122 {
123 // Get the handler's configuration.
124 // This will decode and validate its properties.
125 ExtendedOperationHandlerCfg config =
126 root.getExtendedOperationHandler(name);
127
128 // Register as a change listener for this handler so that we can be
129 // notified when it is disabled or enabled.
130 config.addChangeListener(this);
131
132 // Ignore this handler if it is disabled.
133 if (config.isEnabled())
134 {
135 // Load the handler's implementation class and initialize it.
136 ExtendedOperationHandler handler = getHandler(config);
137
138 // Put this handler in the hash map so that we will be able to find
139 // it if it is deleted or disabled.
140 handlers.put(config.dn(), handler);
141 }
142 }
143 }
144
145 /**
146 * {@inheritDoc}
147 */
148 public ConfigChangeResult applyConfigurationDelete(
149 ExtendedOperationHandlerCfg configuration)
150 {
151 ResultCode resultCode = ResultCode.SUCCESS;
152 boolean adminActionRequired = false;
153
154
155 // See if the entry is registered as an extended operation handler. If so,
156 // deregister it and finalize the handler.
157 ExtendedOperationHandler handler = handlers.remove(configuration.dn());
158 if (handler != null)
159 {
160 handler.finalizeExtendedOperationHandler();
161 }
162
163
164 return new ConfigChangeResult(resultCode, adminActionRequired);
165 }
166
167 /**
168 * {@inheritDoc}
169 */
170 public boolean isConfigurationChangeAcceptable(
171 ExtendedOperationHandlerCfg configuration,
172 List<Message> unacceptableReasons)
173 {
174 if (configuration.isEnabled()) {
175 // It's enabled so always validate the class.
176 return isJavaClassAcceptable(configuration, unacceptableReasons);
177 } else {
178 // It's disabled so ignore it.
179 return true;
180 }
181 }
182
183 /**
184 * {@inheritDoc}
185 */
186 public ConfigChangeResult applyConfigurationChange(
187 ExtendedOperationHandlerCfg configuration)
188 {
189 // Attempt to get the existing handler. This will only
190 // succeed if it was enabled.
191 DN dn = configuration.dn();
192 ExtendedOperationHandler handler = handlers.get(dn);
193
194 // Default result code.
195 ResultCode resultCode = ResultCode.SUCCESS;
196 boolean adminActionRequired = false;
197 ArrayList<Message> messages = new ArrayList<Message>();
198
199 // See whether the handler should be enabled.
200 if (handler == null) {
201 if (configuration.isEnabled()) {
202 // The handler needs to be enabled.
203 try {
204 handler = getHandler(configuration);
205
206 // Put this handler in the hash so that we will
207 // be able to find it if it is altered.
208 handlers.put(dn, handler);
209
210 } catch (ConfigException e) {
211 if (debugEnabled())
212 {
213 TRACER.debugCaught(DebugLogLevel.ERROR, e);
214 }
215
216 messages.add(e.getMessageObject());
217 resultCode = DirectoryServer.getServerErrorResultCode();
218 } catch (Exception e) {
219 if (debugEnabled())
220 {
221 TRACER.debugCaught(DebugLogLevel.ERROR, e);
222 }
223
224 messages.add(ERR_CONFIG_EXTOP_INITIALIZATION_FAILED.get(
225 String.valueOf(configuration.getJavaClass()),
226 String.valueOf(dn),
227 stackTraceToSingleLineString(e)));
228 resultCode = DirectoryServer.getServerErrorResultCode();
229 }
230 }
231 } else {
232 if (configuration.isEnabled()) {
233 // The handler is currently active, so we don't
234 // need to do anything. Changes to the class name cannot be
235 // applied dynamically, so if the class name did change then
236 // indicate that administrative action is required for that
237 // change to take effect.
238 String className = configuration.getJavaClass();
239 if (!className.equals(handler.getClass().getName())) {
240 adminActionRequired = true;
241 }
242 } else {
243 // We need to disable the connection handler.
244
245 handlers.remove(dn);
246
247 handler.finalizeExtendedOperationHandler();
248 }
249 }
250
251 // Return the configuration result.
252 return new ConfigChangeResult(resultCode, adminActionRequired,
253 messages);
254 }
255
256 /**
257 * {@inheritDoc}
258 */
259 public boolean isConfigurationAddAcceptable(
260 ExtendedOperationHandlerCfg configuration,
261 List<Message> unacceptableReasons)
262 {
263 return isConfigurationChangeAcceptable(configuration, unacceptableReasons);
264 }
265
266 /**
267 * {@inheritDoc}
268 */
269 public ConfigChangeResult applyConfigurationAdd(
270 ExtendedOperationHandlerCfg configuration)
271 {
272 // Default result code.
273 ResultCode resultCode = ResultCode.SUCCESS;
274 boolean adminActionRequired = false;
275 ArrayList<Message> messages = new ArrayList<Message>();
276
277 // Register as a change listener for this connection handler entry
278 // so that we will be notified of any changes that may be made to
279 // it.
280 configuration.addChangeListener(this);
281
282 // Ignore this connection handler if it is disabled.
283 if (configuration.isEnabled())
284 {
285 // The connection handler needs to be enabled.
286 DN dn = configuration.dn();
287 try {
288 ExtendedOperationHandler handler = getHandler(configuration);
289
290 // Put this connection handler in the hash so that we will be
291 // able to find it if it is altered.
292 handlers.put(dn, handler);
293
294 }
295 catch (ConfigException e)
296 {
297 if (debugEnabled())
298 {
299 TRACER.debugCaught(DebugLogLevel.ERROR, e);
300 }
301
302 messages.add(e.getMessageObject());
303 resultCode = DirectoryServer.getServerErrorResultCode();
304 }
305 catch (Exception e)
306 {
307 if (debugEnabled())
308 {
309 TRACER.debugCaught(DebugLogLevel.ERROR, e);
310 }
311
312 messages.add(ERR_CONFIG_EXTOP_INITIALIZATION_FAILED.get(
313 String.valueOf(configuration.getJavaClass()),
314 String.valueOf(dn),
315 stackTraceToSingleLineString(e)));
316 resultCode = DirectoryServer.getServerErrorResultCode();
317 }
318 }
319
320 // Return the configuration result.
321 return new ConfigChangeResult(resultCode, adminActionRequired,
322 messages);
323 }
324
325 /**
326 * {@inheritDoc}
327 */
328 public boolean isConfigurationDeleteAcceptable(
329 ExtendedOperationHandlerCfg configuration,
330 List<Message> unacceptableReasons)
331 {
332 // A delete should always be acceptable, so just return true.
333 return true;
334 }
335
336 // Load and initialize the handler named in the config.
337 private ExtendedOperationHandler getHandler(
338 ExtendedOperationHandlerCfg config) throws ConfigException
339 {
340 String className = config.getJavaClass();
341 ExtendedOperationHandlerCfgDefn d =
342 ExtendedOperationHandlerCfgDefn.getInstance();
343 ClassPropertyDefinition pd = d
344 .getJavaClassPropertyDefinition();
345
346 // Load the class and cast it to an extended operation handler.
347 Class<? extends ExtendedOperationHandler> theClass;
348 ExtendedOperationHandler extendedOperationHandler;
349
350 try
351 {
352 theClass = pd.loadClass(className, ExtendedOperationHandler.class);
353 extendedOperationHandler = theClass.newInstance();
354
355 // Determine the initialization method to use: it must take a
356 // single parameter which is the exact type of the configuration
357 // object.
358 Method method = theClass.getMethod("initializeExtendedOperationHandler",
359 config.configurationClass());
360
361 method.invoke(extendedOperationHandler, config);
362 }
363 catch (Exception e)
364 {
365 if (debugEnabled())
366 {
367 TRACER.debugCaught(DebugLogLevel.ERROR, e);
368 }
369
370 Message message = ERR_CONFIG_EXTOP_INVALID_CLASS.
371 get(String.valueOf(className), String.valueOf(config.dn()),
372 String.valueOf(e));
373 throw new ConfigException(message, e);
374 }
375
376 // The handler has been successfully initialized.
377 return extendedOperationHandler;
378 }
379
380
381
382 // Determines whether or not the new configuration's implementation
383 // class is acceptable.
384 private boolean isJavaClassAcceptable(ExtendedOperationHandlerCfg config,
385 List<Message> unacceptableReasons)
386 {
387 String className = config.getJavaClass();
388 ExtendedOperationHandlerCfgDefn d =
389 ExtendedOperationHandlerCfgDefn.getInstance();
390 ClassPropertyDefinition pd = d
391 .getJavaClassPropertyDefinition();
392
393 // Load the class and cast it to an extended operation handler.
394 Class<? extends ExtendedOperationHandler> theClass;
395 try {
396 theClass = pd.loadClass(className, ExtendedOperationHandler.class);
397 ExtendedOperationHandler extOpHandler = theClass.newInstance();
398
399 // Determine the initialization method to use: it must take a
400 // single parameter which is the exact type of the configuration
401 // object.
402 Method method = theClass.getMethod("isConfigurationAcceptable",
403 ExtendedOperationHandlerCfg.class,
404 List.class);
405 Boolean acceptable = (Boolean) method.invoke(extOpHandler, config,
406 unacceptableReasons);
407
408 if (! acceptable)
409 {
410 return false;
411 }
412 }
413 catch (Exception e)
414 {
415 if (debugEnabled())
416 {
417 TRACER.debugCaught(DebugLogLevel.ERROR, e);
418 }
419
420 unacceptableReasons.add(ERR_CONFIG_EXTOP_INVALID_CLASS.get(className,
421 String.valueOf(config.dn()),
422 String.valueOf(e)));
423 return false;
424 }
425
426 // The class is valid as far as we can tell.
427 return true;
428 }
429 }
430