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.tasks; 028 import org.opends.messages.Message; 029 import org.opends.messages.TaskMessages; 030 031 import static org.opends.server.core.DirectoryServer.getAttributeType; 032 import static org.opends.server.config.ConfigConstants.*; 033 import static org.opends.messages.TaskMessages.*; 034 import static org.opends.messages.ToolMessages.*; 035 import static org.opends.server.util.StaticUtils.*; 036 import org.opends.server.backends.task.Task; 037 import org.opends.server.backends.task.TaskState; 038 import org.opends.server.core.DirectoryServer; 039 import org.opends.server.core.LockFileManager; 040 import org.opends.server.api.Backend; 041 import org.opends.server.api.ClientConnection; 042 import org.opends.server.types.Attribute; 043 import org.opends.server.types.AttributeType; 044 import org.opends.server.types.DirectoryException; 045 import org.opends.server.types.DN; 046 import org.opends.server.types.Entry; 047 import org.opends.server.types.ExistingFileBehavior; 048 import org.opends.server.types.LDIFExportConfig; 049 import org.opends.server.types.Operation; 050 import org.opends.server.types.Privilege; 051 import org.opends.server.types.ResultCode; 052 import org.opends.server.types.SearchFilter; 053 054 import java.util.ArrayList; 055 import java.util.HashSet; 056 import java.util.List; 057 import java.util.Map; 058 import java.util.HashMap; 059 import java.io.File; 060 061 /** 062 * This class provides an implementation of a Directory Server task that can 063 * be used to export the contents of a Directory Server backend to an LDIF file. 064 */ 065 public class ExportTask extends Task 066 { 067 068 /** 069 * Stores mapping between configuration attribute name and its label. 070 */ 071 static private Map<String,Message> argDisplayMap = 072 new HashMap<String,Message>(); 073 static { 074 argDisplayMap.put( 075 ATTR_TASK_EXPORT_LDIF_FILE, 076 INFO_EXPORT_ARG_LDIF_FILE.get()); 077 078 argDisplayMap.put( 079 ATTR_TASK_EXPORT_BACKEND_ID, 080 INFO_EXPORT_ARG_BACKEND_ID.get()); 081 082 argDisplayMap.put( 083 ATTR_TASK_EXPORT_APPEND_TO_LDIF, 084 INFO_EXPORT_ARG_APPEND_TO_LDIF.get()); 085 086 argDisplayMap.put( 087 ATTR_TASK_EXPORT_COMPRESS_LDIF, 088 INFO_EXPORT_ARG_COMPRESS_LDIF.get()); 089 090 argDisplayMap.put( 091 ATTR_TASK_EXPORT_ENCRYPT_LDIF, 092 INFO_EXPORT_ARG_ENCRYPT_LDIF.get()); 093 094 argDisplayMap.put( 095 ATTR_TASK_EXPORT_SIGN_HASH, 096 INFO_EXPORT_ARG_SIGN_HASH.get()); 097 098 argDisplayMap.put( 099 ATTR_TASK_EXPORT_INCLUDE_ATTRIBUTE, 100 INFO_EXPORT_ARG_INCL_ATTR.get()); 101 102 argDisplayMap.put( 103 ATTR_TASK_EXPORT_EXCLUDE_ATTRIBUTE, 104 INFO_EXPORT_ARG_EXCL_ATTR.get()); 105 106 argDisplayMap.put( 107 ATTR_TASK_EXPORT_INCLUDE_FILTER, 108 INFO_EXPORT_ARG_INCL_FILTER.get()); 109 110 argDisplayMap.put( 111 ATTR_TASK_EXPORT_EXCLUDE_FILTER, 112 INFO_EXPORT_ARG_EXCL_FILTER.get()); 113 114 argDisplayMap.put( 115 ATTR_TASK_EXPORT_INCLUDE_BRANCH, 116 INFO_EXPORT_ARG_INCL_BRANCH.get()); 117 118 argDisplayMap.put( 119 ATTR_TASK_EXPORT_EXCLUDE_BRANCH, 120 INFO_EXPORT_ARG_EXCL_BRANCH.get()); 121 122 argDisplayMap.put( 123 ATTR_TASK_EXPORT_WRAP_COLUMN, 124 INFO_EXPORT_ARG_WRAP_COLUMN.get()); 125 } 126 127 private String ldifFile; 128 private String backendID; 129 private int wrapColumn; 130 private boolean appendToLDIF; 131 private boolean compressLDIF; 132 private boolean encryptLDIF; 133 private boolean signHash; 134 private boolean includeOperationalAttributes; 135 private ArrayList<String> includeAttributeStrings; 136 private ArrayList<String> excludeAttributeStrings; 137 private ArrayList<String> includeFilterStrings; 138 private ArrayList<String> excludeFilterStrings; 139 private ArrayList<String> includeBranchStrings; 140 private ArrayList<String> excludeBranchStrings; 141 142 private LDIFExportConfig exportConfig; 143 144 /** 145 * {@inheritDoc} 146 */ 147 public Message getDisplayName() { 148 return INFO_TASK_EXPORT_NAME.get(); 149 } 150 151 /** 152 * {@inheritDoc} 153 */ 154 public Message getAttributeDisplayName(String name) { 155 return argDisplayMap.get(name); 156 } 157 158 /** 159 * {@inheritDoc} 160 */ 161 @Override public void initializeTask() throws DirectoryException 162 { 163 // If the client connection is available, then make sure the associated 164 // client has the LDIF_EXPORT privilege. 165 Operation operation = getOperation(); 166 if (operation != null) 167 { 168 ClientConnection clientConnection = operation.getClientConnection(); 169 if (! clientConnection.hasPrivilege(Privilege.LDIF_EXPORT, operation)) 170 { 171 Message message = ERR_TASK_LDIFEXPORT_INSUFFICIENT_PRIVILEGES.get(); 172 throw new DirectoryException(ResultCode.INSUFFICIENT_ACCESS_RIGHTS, 173 message); 174 } 175 } 176 177 178 Entry taskEntry = getTaskEntry(); 179 180 AttributeType typeLdifFile; 181 AttributeType typeBackendID; 182 AttributeType typeAppendToLDIF; 183 AttributeType typeCompressLDIF; 184 AttributeType typeEncryptLDIF; 185 AttributeType typeSignHash; 186 AttributeType typeIncludeAttribute; 187 AttributeType typeExcludeAttribute; 188 AttributeType typeIncludeFilter; 189 AttributeType typeExcludeFilter; 190 AttributeType typeIncludeBranch; 191 AttributeType typeExcludeBranch; 192 AttributeType typeWrapColumn; 193 AttributeType typeIncludeOperationalAttributes; 194 195 196 typeLdifFile = 197 getAttributeType(ATTR_TASK_EXPORT_LDIF_FILE, true); 198 typeBackendID = 199 getAttributeType(ATTR_TASK_EXPORT_BACKEND_ID, true); 200 typeAppendToLDIF = 201 getAttributeType(ATTR_TASK_EXPORT_APPEND_TO_LDIF, true); 202 typeCompressLDIF = 203 getAttributeType(ATTR_TASK_EXPORT_COMPRESS_LDIF, true); 204 typeEncryptLDIF = 205 getAttributeType(ATTR_TASK_EXPORT_ENCRYPT_LDIF, true); 206 typeSignHash = 207 getAttributeType(ATTR_TASK_EXPORT_SIGN_HASH, true); 208 typeIncludeAttribute = 209 getAttributeType(ATTR_TASK_EXPORT_INCLUDE_ATTRIBUTE, true); 210 typeExcludeAttribute = 211 getAttributeType(ATTR_TASK_EXPORT_EXCLUDE_ATTRIBUTE, true); 212 typeIncludeFilter = 213 getAttributeType(ATTR_TASK_EXPORT_INCLUDE_FILTER, true); 214 typeExcludeFilter = 215 getAttributeType(ATTR_TASK_EXPORT_EXCLUDE_FILTER, true); 216 typeIncludeBranch = 217 getAttributeType(ATTR_TASK_EXPORT_INCLUDE_BRANCH, true); 218 typeExcludeBranch = 219 getAttributeType(ATTR_TASK_EXPORT_EXCLUDE_BRANCH, true); 220 typeWrapColumn = 221 getAttributeType(ATTR_TASK_EXPORT_WRAP_COLUMN, true); 222 typeIncludeOperationalAttributes = 223 getAttributeType(ATTR_TASK_EXPORT_INCLUDE_OPERATIONAL_ATTRIBUTES, true); 224 225 226 List<Attribute> attrList; 227 228 attrList = taskEntry.getAttribute(typeLdifFile); 229 ldifFile = TaskUtils.getSingleValueString(attrList); 230 231 attrList = taskEntry.getAttribute(typeBackendID); 232 backendID = TaskUtils.getSingleValueString(attrList); 233 234 attrList = taskEntry.getAttribute(typeAppendToLDIF); 235 appendToLDIF = TaskUtils.getBoolean(attrList, false); 236 237 attrList = taskEntry.getAttribute(typeCompressLDIF); 238 compressLDIF = TaskUtils.getBoolean(attrList, false); 239 240 attrList = taskEntry.getAttribute(typeEncryptLDIF); 241 encryptLDIF = TaskUtils.getBoolean(attrList, false); 242 243 attrList = taskEntry.getAttribute(typeSignHash); 244 signHash = TaskUtils.getBoolean(attrList, false); 245 246 attrList = taskEntry.getAttribute(typeIncludeAttribute); 247 includeAttributeStrings = TaskUtils.getMultiValueString(attrList); 248 249 attrList = taskEntry.getAttribute(typeExcludeAttribute); 250 excludeAttributeStrings = TaskUtils.getMultiValueString(attrList); 251 252 attrList = taskEntry.getAttribute(typeIncludeFilter); 253 includeFilterStrings = TaskUtils.getMultiValueString(attrList); 254 255 attrList = taskEntry.getAttribute(typeExcludeFilter); 256 excludeFilterStrings = TaskUtils.getMultiValueString(attrList); 257 258 attrList = taskEntry.getAttribute(typeIncludeBranch); 259 includeBranchStrings = TaskUtils.getMultiValueString(attrList); 260 261 attrList = taskEntry.getAttribute(typeExcludeBranch); 262 excludeBranchStrings = TaskUtils.getMultiValueString(attrList); 263 264 attrList = taskEntry.getAttribute(typeWrapColumn); 265 wrapColumn = TaskUtils.getSingleValueInteger(attrList, 0); 266 267 attrList = taskEntry.getAttribute(typeIncludeOperationalAttributes); 268 includeOperationalAttributes = TaskUtils.getBoolean(attrList, true); 269 270 } 271 272 273 /** 274 * {@inheritDoc} 275 */ 276 public void interruptTask(TaskState interruptState, Message interruptReason) 277 { 278 if (TaskState.STOPPED_BY_ADMINISTRATOR.equals(interruptState) && 279 exportConfig != null) 280 { 281 addLogMessage(TaskMessages.INFO_TASK_STOPPED_BY_ADMIN.get( 282 interruptReason)); 283 setTaskInterruptState(interruptState); 284 exportConfig.cancel(); 285 } 286 } 287 288 289 /** 290 * {@inheritDoc} 291 */ 292 public boolean isInterruptable() { 293 return true; 294 } 295 296 297 /** 298 * {@inheritDoc} 299 */ 300 protected TaskState runTask() 301 { 302 // See if there were any user-defined sets of include/exclude attributes or 303 // filters. If so, then process them. 304 HashSet<AttributeType> excludeAttributes; 305 if (excludeAttributeStrings == null) 306 { 307 excludeAttributes = null; 308 } 309 else 310 { 311 excludeAttributes = new HashSet<AttributeType>(); 312 for (String attrName : excludeAttributeStrings) 313 { 314 String lowerName = attrName.toLowerCase(); 315 AttributeType attrType = DirectoryServer.getAttributeType(lowerName); 316 if (attrType == null) 317 { 318 attrType = DirectoryServer.getDefaultAttributeType(attrName); 319 } 320 321 excludeAttributes.add(attrType); 322 } 323 } 324 325 HashSet<AttributeType> includeAttributes; 326 if (includeAttributeStrings == null) 327 { 328 includeAttributes = null; 329 } 330 else 331 { 332 includeAttributes = new HashSet<AttributeType>(); 333 for (String attrName : includeAttributeStrings) 334 { 335 String lowerName = attrName.toLowerCase(); 336 AttributeType attrType = DirectoryServer.getAttributeType(lowerName); 337 if (attrType == null) 338 { 339 attrType = DirectoryServer.getDefaultAttributeType(attrName); 340 } 341 342 includeAttributes.add(attrType); 343 } 344 } 345 346 ArrayList<SearchFilter> excludeFilters; 347 if (excludeFilterStrings == null) 348 { 349 excludeFilters = null; 350 } 351 else 352 { 353 excludeFilters = new ArrayList<SearchFilter>(); 354 for (String filterString : excludeFilterStrings) 355 { 356 try 357 { 358 excludeFilters.add(SearchFilter.createFilterFromString(filterString)); 359 } 360 catch (DirectoryException de) 361 { 362 Message message = ERR_LDIFEXPORT_CANNOT_PARSE_EXCLUDE_FILTER.get( 363 filterString, de.getMessageObject()); 364 logError(message); 365 return TaskState.STOPPED_BY_ERROR; 366 } 367 catch (Exception e) 368 { 369 Message message = ERR_LDIFEXPORT_CANNOT_PARSE_EXCLUDE_FILTER.get( 370 filterString, getExceptionMessage(e)); 371 logError(message); 372 return TaskState.STOPPED_BY_ERROR; 373 } 374 } 375 } 376 377 ArrayList<SearchFilter> includeFilters; 378 if (includeFilterStrings == null) 379 { 380 includeFilters = null; 381 } 382 else 383 { 384 includeFilters = new ArrayList<SearchFilter>(); 385 for (String filterString : includeFilterStrings) 386 { 387 try 388 { 389 includeFilters.add(SearchFilter.createFilterFromString(filterString)); 390 } 391 catch (DirectoryException de) 392 { 393 Message message = ERR_LDIFEXPORT_CANNOT_PARSE_INCLUDE_FILTER.get( 394 filterString, de.getMessageObject()); 395 logError(message); 396 return TaskState.STOPPED_BY_ERROR; 397 } 398 catch (Exception e) 399 { 400 Message message = ERR_LDIFEXPORT_CANNOT_PARSE_INCLUDE_FILTER.get( 401 filterString, getExceptionMessage(e)); 402 logError(message); 403 return TaskState.STOPPED_BY_ERROR; 404 } 405 } 406 } 407 408 // Get the backend into which the LDIF should be imported. 409 Backend backend; 410 ArrayList<DN> defaultIncludeBranches; 411 412 backend = DirectoryServer.getBackend(backendID); 413 414 if (backend == null) 415 { 416 Message message = ERR_LDIFEXPORT_NO_BACKENDS_FOR_ID.get(backendID); 417 logError(message); 418 return TaskState.STOPPED_BY_ERROR; 419 } 420 else if (! backend.supportsLDIFExport()) 421 { 422 Message message = ERR_LDIFEXPORT_CANNOT_EXPORT_BACKEND.get(backendID); 423 logError(message); 424 return TaskState.STOPPED_BY_ERROR; 425 } 426 427 defaultIncludeBranches = new ArrayList<DN>(backend.getBaseDNs().length); 428 for (DN dn : backend.getBaseDNs()) 429 { 430 defaultIncludeBranches.add(dn); 431 } 432 433 ArrayList<DN> excludeBranches = new ArrayList<DN>(); 434 if (excludeBranchStrings != null) 435 { 436 for (String s : excludeBranchStrings) 437 { 438 DN excludeBranch; 439 try 440 { 441 excludeBranch = DN.decode(s); 442 } 443 catch (DirectoryException de) 444 { 445 Message message = ERR_LDIFEXPORT_CANNOT_DECODE_EXCLUDE_BASE.get( 446 s, de.getMessageObject()); 447 logError(message); 448 return TaskState.STOPPED_BY_ERROR; 449 } 450 catch (Exception e) 451 { 452 Message message = ERR_LDIFEXPORT_CANNOT_DECODE_EXCLUDE_BASE.get( 453 s, getExceptionMessage(e)); 454 logError(message); 455 return TaskState.STOPPED_BY_ERROR; 456 } 457 458 if (! excludeBranches.contains(excludeBranch)) 459 { 460 excludeBranches.add(excludeBranch); 461 } 462 } 463 } 464 465 466 ArrayList<DN> includeBranches; 467 if (!includeBranchStrings.isEmpty()) 468 { 469 includeBranches = new ArrayList<DN>(); 470 for (String s : includeBranchStrings) 471 { 472 DN includeBranch; 473 try 474 { 475 includeBranch = DN.decode(s); 476 } 477 catch (DirectoryException de) 478 { 479 Message message = ERR_LDIFIMPORT_CANNOT_DECODE_INCLUDE_BASE.get( 480 s, de.getMessageObject()); 481 logError(message); 482 return TaskState.STOPPED_BY_ERROR; 483 } 484 catch (Exception e) 485 { 486 Message message = ERR_LDIFIMPORT_CANNOT_DECODE_INCLUDE_BASE.get( 487 s, getExceptionMessage(e)); 488 logError(message); 489 return TaskState.STOPPED_BY_ERROR; 490 } 491 492 if (! Backend.handlesEntry(includeBranch, defaultIncludeBranches, 493 excludeBranches)) 494 { 495 Message message = 496 ERR_LDIFEXPORT_INVALID_INCLUDE_BASE.get(s, backendID); 497 logError(message); 498 return TaskState.STOPPED_BY_ERROR; 499 } 500 501 includeBranches.add(includeBranch); 502 } 503 } 504 else 505 { 506 includeBranches = defaultIncludeBranches; 507 } 508 509 510 // Create the LDIF export configuration to use when reading the LDIF. 511 ExistingFileBehavior existingBehavior; 512 if (appendToLDIF) 513 { 514 existingBehavior = ExistingFileBehavior.APPEND; 515 } 516 else 517 { 518 existingBehavior = ExistingFileBehavior.OVERWRITE; 519 } 520 521 exportConfig = new LDIFExportConfig(ldifFile, existingBehavior); 522 exportConfig.setCompressData(compressLDIF); 523 exportConfig.setEncryptData(encryptLDIF); 524 exportConfig.setExcludeAttributes(excludeAttributes); 525 exportConfig.setExcludeBranches(excludeBranches); 526 exportConfig.setExcludeFilters(excludeFilters); 527 exportConfig.setIncludeAttributes(includeAttributes); 528 exportConfig.setIncludeBranches(includeBranches); 529 exportConfig.setIncludeFilters(includeFilters); 530 exportConfig.setSignHash(signHash); 531 exportConfig.setWrapColumn(wrapColumn); 532 exportConfig.setIncludeOperationalAttributes(includeOperationalAttributes); 533 534 // FIXME -- Should this be conditional? 535 exportConfig.setInvokeExportPlugins(true); 536 537 538 // Get the set of base DNs for the backend as an array. 539 DN[] baseDNs = new DN[defaultIncludeBranches.size()]; 540 defaultIncludeBranches.toArray(baseDNs); 541 542 543 // From here we must make sure we close the export config. 544 try 545 { 546 // Acquire a shared lock for the backend. 547 try 548 { 549 String lockFile = LockFileManager.getBackendLockFileName(backend); 550 StringBuilder failureReason = new StringBuilder(); 551 if (! LockFileManager.acquireSharedLock(lockFile, failureReason)) 552 { 553 Message message = ERR_LDIFEXPORT_CANNOT_LOCK_BACKEND.get( 554 backend.getBackendID(), String.valueOf(failureReason)); 555 logError(message); 556 return TaskState.STOPPED_BY_ERROR; 557 } 558 } 559 catch (Exception e) 560 { 561 Message message = ERR_LDIFEXPORT_CANNOT_LOCK_BACKEND.get( 562 backend.getBackendID(), getExceptionMessage(e)); 563 logError(message); 564 return TaskState.STOPPED_BY_ERROR; 565 } 566 567 568 // From here we must make sure we release the shared backend lock. 569 try 570 { 571 // Launch the export. 572 try 573 { 574 DirectoryServer.notifyExportBeginning(backend, exportConfig); 575 backend.exportLDIF(exportConfig); 576 DirectoryServer.notifyExportEnded(backend, exportConfig, true); 577 } 578 catch (DirectoryException de) 579 { 580 DirectoryServer.notifyExportEnded(backend, exportConfig, false); 581 Message message = 582 ERR_LDIFEXPORT_ERROR_DURING_EXPORT.get(de.getMessageObject()); 583 logError(message); 584 return TaskState.STOPPED_BY_ERROR; 585 } 586 catch (Exception e) 587 { 588 DirectoryServer.notifyExportEnded(backend, exportConfig, false); 589 Message message = 590 ERR_LDIFEXPORT_ERROR_DURING_EXPORT.get(getExceptionMessage(e)); 591 logError(message); 592 return TaskState.STOPPED_BY_ERROR; 593 } 594 } 595 finally 596 { 597 // Release the shared lock on the backend. 598 try 599 { 600 String lockFile = LockFileManager.getBackendLockFileName(backend); 601 StringBuilder failureReason = new StringBuilder(); 602 if (! LockFileManager.releaseLock(lockFile, failureReason)) 603 { 604 Message message = WARN_LDIFEXPORT_CANNOT_UNLOCK_BACKEND.get( 605 backend.getBackendID(), String.valueOf(failureReason)); 606 logError(message); 607 return TaskState.COMPLETED_WITH_ERRORS; 608 } 609 } 610 catch (Exception e) 611 { 612 Message message = WARN_LDIFEXPORT_CANNOT_UNLOCK_BACKEND.get( 613 backend.getBackendID(), getExceptionMessage(e)); 614 logError(message); 615 return TaskState.COMPLETED_WITH_ERRORS; 616 } 617 } 618 } 619 finally 620 { 621 // Clean up after the export by closing the export config. 622 exportConfig.close(); 623 } 624 625 // If the operation was cancelled delete the export file since 626 // if will be incomplete. 627 if (exportConfig.isCancelled()) 628 { 629 File f = new File(ldifFile); 630 if (f.exists()) 631 { 632 f.delete(); 633 } 634 } 635 636 // If we got here the task either completed successfully or 637 // was interrupted 638 return getFinalTaskState(); 639 } 640 }