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 2007-2008 Sun Microsystems, Inc.
026 */
027 package org.opends.server.workflowelement;
028
029
030
031 import static org.opends.server.util.StaticUtils.stackTraceToSingleLineString;
032 import static org.opends.messages.ConfigMessages.*;
033
034 import java.lang.reflect.Method;
035 import java.util.ArrayList;
036 import java.util.Iterator;
037 import java.util.List;
038 import java.util.concurrent.ConcurrentHashMap;
039
040 import org.opends.messages.Message;
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.WorkflowElementCfgDefn;
047 import org.opends.server.admin.std.server.RootCfg;
048 import org.opends.server.admin.std.server.WorkflowElementCfg;
049 import org.opends.server.config.ConfigException;
050 import org.opends.server.core.DirectoryServer;
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 * This class defines a utility that will be used to manage the configuration
059 * for the set of workflow elements defined in the Directory Server.
060 * It will perform the necessary initialization of those backends when the
061 * server is first started, and then will manage any changes to them while
062 * the server is running.
063 */
064 public class WorkflowElementConfigManager
065 implements ConfigurationChangeListener<WorkflowElementCfg>,
066 ConfigurationAddListener <WorkflowElementCfg>,
067 ConfigurationDeleteListener<WorkflowElementCfg>
068
069 {
070 // A mapping between the DNs of the config entries and the associated
071 // workflow elements.
072 private ConcurrentHashMap<DN, WorkflowElement> workflowElements;
073
074
075
076 /**
077 * Creates a new instance of this workflow config manager.
078 */
079 public WorkflowElementConfigManager()
080 {
081 workflowElements = new ConcurrentHashMap<DN, WorkflowElement>();
082 }
083
084
085
086 /**
087 * Initializes all workflow elements currently defined in the Directory
088 * Server configuration. This should only be called at Directory Server
089 * startup.
090 *
091 * @throws ConfigException If a configuration problem causes the workflow
092 * element initialization process to fail.
093 * @throws InitializationException If a problem occurs while the workflow
094 * element is loaded and registered with
095 * the server
096 */
097 public void initializeWorkflowElements()
098 throws ConfigException, InitializationException
099 {
100 // Get the root configuration object.
101 ServerManagementContext managementContext =
102 ServerManagementContext.getInstance();
103 RootCfg rootConfiguration =
104 managementContext.getRootConfiguration();
105
106
107 // Register as an add and delete listener with the root configuration so we
108 // can be notified if any workflow element entries are added or removed.
109 rootConfiguration.addWorkflowElementAddListener(this);
110 rootConfiguration.addWorkflowElementDeleteListener(this);
111
112
113 //Initialize the existing workflows.
114 for (String workflowName : rootConfiguration.listWorkflowElements())
115 {
116 WorkflowElementCfg workflowConfiguration =
117 rootConfiguration.getWorkflowElement(workflowName);
118 workflowConfiguration.addChangeListener(this);
119
120 if (workflowConfiguration.isEnabled())
121 {
122 loadAndRegisterWorkflowElement(workflowConfiguration);
123 }
124 }
125 }
126
127
128
129 /**
130 * {@inheritDoc}
131 */
132 public boolean isConfigurationAddAcceptable(
133 WorkflowElementCfg configuration,
134 List<Message> unacceptableReasons)
135 {
136 boolean isAcceptable = true;
137
138 if (configuration.isEnabled())
139 {
140 // Get the name of the class and make sure we can instantiate it as
141 // a workflow element.
142 String className = configuration.getJavaClass();
143 try
144 {
145 // Load the class but don't initialize it.
146 loadWorkflowElement(className, configuration, false);
147 }
148 catch (InitializationException ie)
149 {
150 unacceptableReasons.add (ie.getMessageObject());
151 isAcceptable = false;
152 }
153 }
154
155 return isAcceptable;
156 }
157
158
159
160 /**
161 * {@inheritDoc}
162 */
163 public ConfigChangeResult applyConfigurationAdd(
164 WorkflowElementCfg configuration)
165 {
166 // Returned result.
167 ConfigChangeResult changeResult = new ConfigChangeResult(
168 ResultCode.SUCCESS, false, new ArrayList<Message>()
169 );
170
171 configuration.addChangeListener(this);
172
173 // If the new workflow element is enabled then create it and register it.
174 if (configuration.isEnabled())
175 {
176 try
177 {
178 loadAndRegisterWorkflowElement(configuration);
179 }
180 catch (InitializationException de)
181 {
182 if (changeResult.getResultCode() == ResultCode.SUCCESS)
183 {
184 changeResult.setResultCode(
185 DirectoryServer.getServerErrorResultCode());
186 }
187 changeResult.addMessage(de.getMessageObject());
188 }
189 }
190
191 return changeResult;
192 }
193
194
195
196 /**
197 * {@inheritDoc}
198 */
199 public boolean isConfigurationDeleteAcceptable(
200 WorkflowElementCfg configuration,
201 List<Message> unacceptableReasons)
202 {
203 // FIXME -- We should try to perform some check to determine whether the
204 // worklfow element is in use.
205 return true;
206 }
207
208
209
210 /**
211 * {@inheritDoc}
212 */
213 public ConfigChangeResult applyConfigurationDelete(
214 WorkflowElementCfg configuration)
215 {
216 // Returned result.
217 ConfigChangeResult changeResult = new ConfigChangeResult(
218 ResultCode.SUCCESS, false, new ArrayList<Message>()
219 );
220
221
222 WorkflowElement workflowElement =
223 workflowElements.remove(configuration.dn());
224 if (workflowElement != null)
225 {
226 workflowElement.deregister();
227 workflowElement.finalizeWorkflowElement();
228 }
229
230
231 return changeResult;
232 }
233
234
235
236 /**
237 * {@inheritDoc}
238 */
239 public boolean isConfigurationChangeAcceptable(
240 WorkflowElementCfg configuration,
241 List<Message> unacceptableReasons)
242 {
243 boolean isAcceptable = true;
244
245 if (configuration.isEnabled())
246 {
247 // Get the name of the class and make sure we can instantiate it as
248 // a workflow element.
249 String className = configuration.getJavaClass();
250 try
251 {
252 // Load the class but don't initialize it.
253 loadWorkflowElement(className, configuration, false);
254 }
255 catch (InitializationException ie)
256 {
257 unacceptableReasons.add (ie.getMessageObject());
258 isAcceptable = false;
259 }
260 }
261
262 return isAcceptable;
263 }
264
265
266
267 /**
268 * {@inheritDoc}
269 */
270 public ConfigChangeResult applyConfigurationChange(
271 WorkflowElementCfg configuration)
272 {
273 // Returned result.
274 ConfigChangeResult changeResult = new ConfigChangeResult(
275 ResultCode.SUCCESS, false, new ArrayList<Message>()
276 );
277
278
279 // Get the existing workflow element if it's already enabled.
280 WorkflowElement existingWorkflowElement =
281 workflowElements.get(configuration.dn());
282
283 // If the new configuration has the workflow element disabled,
284 // then disable it if it is enabled, or do nothing if it's already disabled.
285 if (! configuration.isEnabled())
286 {
287 if (existingWorkflowElement != null)
288 {
289 workflowElements.remove(configuration.dn());
290 existingWorkflowElement.deregister();
291 existingWorkflowElement.finalizeWorkflowElement();
292 }
293
294 return changeResult;
295 }
296
297 // If the workflow element is disabled then create it and register it.
298 if (existingWorkflowElement == null)
299 {
300 try
301 {
302 loadAndRegisterWorkflowElement(configuration);
303 }
304 catch (InitializationException de)
305 {
306 if (changeResult.getResultCode() == ResultCode.SUCCESS)
307 {
308 changeResult.setResultCode(
309 DirectoryServer.getServerErrorResultCode());
310 }
311 changeResult.addMessage(de.getMessageObject());
312 }
313 }
314
315 return changeResult;
316 }
317
318
319 /**
320 * Loads a class and instanciates it as a workflow element. The workflow
321 * element is initialized and registered with the server.
322 *
323 * @param workflowCfg the workflow element configuration
324 *
325 * @throws InitializationException If a problem occurs while trying to
326 * decode a provided string as a DN or if
327 * the workflow element ID for a provided
328 * workflow element conflicts with the workflow
329 * ID of an existing workflow during workflow
330 * registration.
331 */
332 private void loadAndRegisterWorkflowElement(
333 WorkflowElementCfg workflowElementCfg
334 ) throws InitializationException
335 {
336 // Load the workflow element class
337 String className = workflowElementCfg.getJavaClass();
338 WorkflowElement workflowElement =
339 loadWorkflowElement(className, workflowElementCfg, true);
340
341 try
342 {
343 // register the workflow element
344 workflowElement.register();
345
346 // keep the workflow element in the list of configured workflow
347 // elements
348 workflowElements.put(workflowElementCfg.dn(), workflowElement);
349 }
350 catch (ConfigException de)
351 {
352 throw new InitializationException(de.getMessageObject());
353 }
354 }
355
356
357 /**
358 * Loads a class and instanciates it as a workflow element. If requested
359 * initializes the newly created instance.
360 *
361 * @param className The fully-qualified name of the workflow element
362 * class to load, instantiate, and initialize.
363 * @param configuration The configuration to use to initialize the workflow
364 * element. It must not be {@code null}.
365 * @param initialize Indicates whether the workflow element instance
366 * should be initialized.
367 *
368 * @return The possibly initialized workflow element.
369 *
370 * @throws InitializationException If a problem occurred while attempting
371 * to initialize the workflow element.
372 */
373 private WorkflowElement loadWorkflowElement(
374 String className,
375 WorkflowElementCfg configuration,
376 boolean initialize
377 ) throws InitializationException
378 {
379 try
380 {
381 WorkflowElementCfgDefn definition;
382 ClassPropertyDefinition propertyDefinition;
383 Class<? extends WorkflowElement> workflowElementClass;
384 WorkflowElement<? extends WorkflowElementCfg> workflowElement;
385
386 definition = WorkflowElementCfgDefn.getInstance();
387 propertyDefinition =
388 definition.getJavaClassPropertyDefinition();
389 workflowElementClass =
390 propertyDefinition.loadClass(className, WorkflowElement.class);
391 workflowElement =
392 (WorkflowElement<? extends WorkflowElementCfg>)
393 workflowElementClass.newInstance();
394
395 if (initialize)
396 {
397 Method method = workflowElement.getClass().getMethod(
398 "initializeWorkflowElement", configuration.configurationClass());
399 method.invoke(workflowElement, configuration);
400 }
401 else
402 {
403 Method method = workflowElement.getClass().getMethod(
404 "isConfigurationAcceptable",
405 WorkflowElementCfg.class,
406 List.class);
407
408 List<String> unacceptableReasons = new ArrayList<String>();
409 Boolean acceptable = (Boolean) method.invoke(
410 workflowElement, configuration, unacceptableReasons);
411
412 if (! acceptable)
413 {
414 StringBuilder buffer = new StringBuilder();
415 if (! unacceptableReasons.isEmpty())
416 {
417 Iterator<String> iterator = unacceptableReasons.iterator();
418 buffer.append(iterator.next());
419 while (iterator.hasNext())
420 {
421 buffer.append(". ");
422 buffer.append(iterator.next());
423 }
424 }
425
426 Message message =
427 ERR_CONFIG_WORKFLOW_ELEMENT_CONFIG_NOT_ACCEPTABLE.get(
428 String.valueOf(configuration.dn()), buffer.toString());
429 throw new InitializationException(message);
430 }
431 }
432
433 return workflowElement;
434 }
435 catch (Exception e)
436 {
437 Message message =
438 ERR_CONFIG_WORKFLOW_ELEMENT_CANNOT_INITIALIZE.get(
439 className, String.valueOf(configuration.dn()),
440 stackTraceToSingleLineString(e));
441 throw new InitializationException(message);
442 }
443 }
444
445 }
446