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