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 2008 Sun Microsystems, Inc. 026 */ 027 package org.opends.server.workflowelement.localbackend; 028 029 030 031 import java.util.ArrayList; 032 import java.util.List; 033 import java.util.TreeMap; 034 035 import org.opends.messages.Message; 036 import org.opends.server.admin.server.ConfigurationChangeListener; 037 import org.opends.server.admin.std.server.LocalBackendWorkflowElementCfg; 038 import org.opends.server.api.Backend; 039 import org.opends.server.config.ConfigException; 040 import org.opends.server.core.AddOperation; 041 import org.opends.server.core.BindOperation; 042 import org.opends.server.core.CompareOperation; 043 import org.opends.server.core.DeleteOperation; 044 import org.opends.server.core.DirectoryServer; 045 import org.opends.server.core.ModifyDNOperation; 046 import org.opends.server.core.ModifyOperation; 047 import org.opends.server.core.SearchOperation; 048 import org.opends.server.types.*; 049 import org.opends.server.workflowelement.LeafWorkflowElement; 050 051 052 053 /** 054 * This class defines a local backend workflow element; e-g an entity that 055 * handle the processing of an operation aginst a local backend. 056 */ 057 public class LocalBackendWorkflowElement extends 058 LeafWorkflowElement<LocalBackendWorkflowElementCfg> 059 implements ConfigurationChangeListener<LocalBackendWorkflowElementCfg> 060 { 061 // the backend associated with the local workflow element 062 private Backend backend; 063 064 065 // the set of local backend workflow elements registered with the server 066 private static TreeMap<String, LocalBackendWorkflowElement> 067 registeredLocalBackends = 068 new TreeMap<String, LocalBackendWorkflowElement>(); 069 070 071 // a lock to guarantee safe concurrent access to the registeredLocalBackends 072 // variable 073 private static Object registeredLocalBackendsLock = new Object(); 074 075 076 /** 077 * Creates a new instance of the local backend workflow element. 078 */ 079 public LocalBackendWorkflowElement() 080 { 081 // There is nothing to do in this constructor. 082 } 083 084 085 /** 086 * Initializes a new instance of the local backend workflow element. 087 * This method is intended to be called by DirectoryServer when 088 * workflow configuration mode is auto as opposed to 089 * initializeWorkflowElement which is invoked when workflow 090 * configuration mode is manual. 091 * 092 * @param workflowElementID the workflow element identifier 093 * @param backend the backend associated to that workflow element 094 */ 095 private void initialize(String workflowElementID, Backend backend) 096 { 097 // Initialize the workflow ID 098 super.initialize(workflowElementID); 099 100 this.backend = backend; 101 102 if (this.backend != null) 103 { 104 setPrivate(this.backend.isPrivateBackend()); 105 } 106 } 107 108 109 /** 110 * Initializes a new instance of the local backend workflow element. 111 * This method is intended to be called by DirectoryServer when 112 * workflow configuration mode is manual as opposed to 113 * initialize(String,Backend) which is invoked when workflow 114 * configuration mode is auto. 115 * 116 * @param configuration The configuration for this local backend 117 * workflow element. 118 * 119 * @throws ConfigException If there is a problem with the provided 120 * configuration. 121 * 122 * @throws InitializationException If an error occurs while trying 123 * to initialize this workflow 124 * element that is not related to 125 * the provided configuration. 126 */ 127 public void initializeWorkflowElement( 128 LocalBackendWorkflowElementCfg configuration 129 ) throws ConfigException, InitializationException 130 { 131 configuration.addLocalBackendChangeListener(this); 132 133 // Read configuration and apply changes. 134 processWorkflowElementConfig(configuration, true); 135 } 136 137 138 /** 139 * {@inheritDoc} 140 */ 141 public void finalizeWorkflowElement() 142 { 143 // null all fields so that any use of the finalized object will raise 144 // an NPE 145 super.initialize(null); 146 backend = null; 147 } 148 149 150 /** 151 * {@inheritDoc} 152 */ 153 public boolean isConfigurationChangeAcceptable( 154 LocalBackendWorkflowElementCfg configuration, 155 List<Message> unacceptableReasons 156 ) 157 { 158 boolean isAcceptable = 159 processWorkflowElementConfig(configuration, false); 160 161 return isAcceptable; 162 } 163 164 165 /** 166 * {@inheritDoc} 167 */ 168 public ConfigChangeResult applyConfigurationChange( 169 LocalBackendWorkflowElementCfg configuration 170 ) 171 { 172 // Returned result. 173 ConfigChangeResult changeResult = new ConfigChangeResult( 174 ResultCode.SUCCESS, false, new ArrayList<Message>() 175 ); 176 177 processWorkflowElementConfig(configuration, true); 178 179 return changeResult; 180 } 181 182 183 /** 184 * Parses the provided configuration and configure the workflow element. 185 * 186 * @param configuration The new configuration containing the changes. 187 * @param applyChanges If true then take into account the new configuration. 188 * 189 * @return <code>true</code> if the configuration is acceptable. 190 */ 191 private boolean processWorkflowElementConfig( 192 LocalBackendWorkflowElementCfg configuration, 193 boolean applyChanges 194 ) 195 { 196 // returned status 197 boolean isAcceptable = true; 198 199 // If the workflow element is disabled then do nothing. Note that the 200 // config manager could have finalized the object right before. 201 if (configuration.isEnabled()) 202 { 203 // Read configuration. 204 String newBackendID = configuration.getBackend(); 205 Backend newBackend = DirectoryServer.getBackend(newBackendID); 206 207 // Get the new config 208 if (applyChanges) 209 { 210 super.initialize(configuration.getWorkflowElementId()); 211 backend = newBackend; 212 } 213 } 214 215 return isAcceptable; 216 } 217 218 219 /** 220 * Creates and registers a local backend with the server. 221 * 222 * @param workflowElementID the identifier of the workflow element to create 223 * @param backend the backend to associate with the local backend 224 * workflow element 225 * 226 * @return the existing local backend workflow element if it was 227 * already created or a newly created local backend workflow 228 * element. 229 */ 230 public static LocalBackendWorkflowElement createAndRegister( 231 String workflowElementID, 232 Backend backend) 233 { 234 LocalBackendWorkflowElement localBackend = null; 235 236 // If the requested workflow element does not exist then create one. 237 localBackend = registeredLocalBackends.get(workflowElementID); 238 if (localBackend == null) 239 { 240 localBackend = new LocalBackendWorkflowElement(); 241 localBackend.initialize(workflowElementID, backend); 242 243 // store the new local backend in the list of registered backends 244 registerLocalBackend(localBackend); 245 } 246 247 return localBackend; 248 } 249 250 251 252 /** 253 * Removes a local backend that was registered with the server. 254 * 255 * @param workflowElementID the identifier of the workflow element to remove 256 */ 257 public static void remove(String workflowElementID) 258 { 259 deregisterLocalBackend(workflowElementID); 260 } 261 262 263 264 /** 265 * Removes all the local backends that were registered with the server. 266 * This function is intended to be called when the server is shutting down. 267 */ 268 public static void removeAll() 269 { 270 synchronized (registeredLocalBackendsLock) 271 { 272 for (LocalBackendWorkflowElement localBackend: 273 registeredLocalBackends.values()) 274 { 275 deregisterLocalBackend(localBackend.getWorkflowElementID()); 276 } 277 } 278 } 279 280 281 282 /** 283 * Registers a local backend with the server. 284 * 285 * @param localBackend the local backend to register with the server 286 */ 287 private static void registerLocalBackend( 288 LocalBackendWorkflowElement localBackend) 289 { 290 synchronized (registeredLocalBackendsLock) 291 { 292 String localBackendID = localBackend.getWorkflowElementID(); 293 LocalBackendWorkflowElement existingLocalBackend = 294 registeredLocalBackends.get(localBackendID); 295 296 if (existingLocalBackend == null) 297 { 298 TreeMap<String, LocalBackendWorkflowElement> newLocalBackends = 299 new TreeMap 300 <String, LocalBackendWorkflowElement>(registeredLocalBackends); 301 newLocalBackends.put(localBackendID, localBackend); 302 registeredLocalBackends = newLocalBackends; 303 } 304 } 305 } 306 307 308 309 /** 310 * Deregisters a local backend with the server. 311 * 312 * @param workflowElementID the identifier of the workflow element to remove 313 */ 314 private static void deregisterLocalBackend(String workflowElementID) 315 { 316 synchronized (registeredLocalBackendsLock) 317 { 318 LocalBackendWorkflowElement existingLocalBackend = 319 registeredLocalBackends.get(workflowElementID); 320 321 if (existingLocalBackend != null) 322 { 323 TreeMap<String, LocalBackendWorkflowElement> newLocalBackends = 324 new TreeMap<String, LocalBackendWorkflowElement>( 325 registeredLocalBackends); 326 newLocalBackends.remove(workflowElementID); 327 registeredLocalBackends = newLocalBackends; 328 } 329 } 330 } 331 332 333 334 /** 335 * {@inheritDoc} 336 */ 337 public void execute(Operation operation) throws CanceledOperationException { 338 switch (operation.getOperationType()) 339 { 340 case BIND: 341 LocalBackendBindOperation bindOperation = 342 new LocalBackendBindOperation((BindOperation) operation); 343 bindOperation.processLocalBind(backend); 344 break; 345 346 case SEARCH: 347 LocalBackendSearchOperation searchOperation = 348 new LocalBackendSearchOperation((SearchOperation) operation); 349 searchOperation.processLocalSearch(backend); 350 break; 351 352 case ADD: 353 LocalBackendAddOperation addOperation = 354 new LocalBackendAddOperation((AddOperation) operation); 355 addOperation.processLocalAdd(backend); 356 break; 357 358 case DELETE: 359 LocalBackendDeleteOperation deleteOperation = 360 new LocalBackendDeleteOperation((DeleteOperation) operation); 361 deleteOperation.processLocalDelete(backend); 362 break; 363 364 case MODIFY: 365 LocalBackendModifyOperation modifyOperation = 366 new LocalBackendModifyOperation((ModifyOperation) operation); 367 modifyOperation.processLocalModify(backend); 368 break; 369 370 case MODIFY_DN: 371 LocalBackendModifyDNOperation modifyDNOperation = 372 new LocalBackendModifyDNOperation((ModifyDNOperation) operation); 373 modifyDNOperation.processLocalModifyDN(backend); 374 break; 375 376 case COMPARE: 377 LocalBackendCompareOperation compareOperation = 378 new LocalBackendCompareOperation((CompareOperation) operation); 379 compareOperation.processLocalCompare(backend); 380 break; 381 382 case ABANDON: 383 // There is no processing for an abandon operation. 384 break; 385 386 default: 387 throw new AssertionError("Attempted to execute an invalid operation " + 388 "type: " + operation.getOperationType() + 389 " (" + operation + ")"); 390 } 391 } 392 393 394 395 /** 396 * Attaches the current local operation to the global operation so that 397 * operation runner can execute local operation post response later on. 398 * 399 * @param <O> subtype of Operation 400 * @param <L> subtype of LocalBackendOperation 401 * @param globalOperation the global operation to which local operation 402 * should be attached to 403 * @param currentLocalOperation the local operation to attach to the global 404 * operation 405 */ 406 @SuppressWarnings("unchecked") 407 public static final <O extends Operation,L> void 408 attachLocalOperation (O globalOperation, L currentLocalOperation) 409 { 410 List<?> existingAttachment = 411 (List<?>) globalOperation.getAttachment(Operation.LOCALBACKENDOPERATIONS); 412 413 List<L> newAttachment = new ArrayList<L>(); 414 415 if (existingAttachment != null) 416 { 417 // This line raises an unchecked conversion warning. 418 // There is nothing we can do to prevent this warning 419 // so let's get rid of it since we know the cast is safe. 420 newAttachment.addAll ((List<L>) existingAttachment); 421 } 422 newAttachment.add (currentLocalOperation); 423 globalOperation.setAttachment(Operation.LOCALBACKENDOPERATIONS, 424 newAttachment); 425 } 426 427 } 428