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