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 }