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