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