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.messages.TaskMessages.*;
032    import static org.opends.messages.ToolMessages.*;
033    import static org.opends.server.loggers.debug.DebugLogger.*;
034    import org.opends.server.loggers.debug.DebugTracer;
035    import org.opends.server.types.DebugLogLevel;
036    import static org.opends.server.util.StaticUtils.*;
037    import static org.opends.server.config.ConfigConstants.*;
038    import static org.opends.server.core.DirectoryServer.getAttributeType;
039    
040    import org.opends.server.backends.task.Task;
041    import org.opends.server.backends.task.TaskState;
042    import org.opends.server.core.DirectoryServer;
043    import org.opends.server.core.LockFileManager;
044    import org.opends.server.api.Backend;
045    import org.opends.server.api.ClientConnection;
046    import org.opends.server.types.Attribute;
047    import org.opends.server.types.AttributeType;
048    import org.opends.server.types.DirectoryException;
049    import org.opends.server.types.DN;
050    import org.opends.server.types.Entry;
051    
052    
053    import org.opends.server.types.ExistingFileBehavior;
054    import org.opends.server.types.LDIFImportConfig;
055    import org.opends.server.types.Operation;
056    import org.opends.server.types.Privilege;
057    import org.opends.server.types.ResultCode;
058    import org.opends.server.types.SearchFilter;
059    
060    import java.util.HashSet;
061    import java.util.ArrayList;
062    import java.util.List;
063    import java.util.Map;
064    import java.util.HashMap;
065    
066    /**
067     * This class provides an implementation of a Directory Server task that can
068     * be used to import data from an LDIF file into a backend.
069     */
070    public class ImportTask extends Task
071    {
072      /**
073       * The tracer object for the debug logger.
074       */
075      private static final DebugTracer TRACER = getTracer();
076    
077    
078      /**
079       * Stores mapping between configuration attribute name and its label.
080       */
081      static private Map<String,Message> argDisplayMap =
082              new HashMap<String,Message>();
083    
084      static {
085        argDisplayMap.put(
086                ATTR_IMPORT_LDIF_FILE,
087                INFO_IMPORT_ARG_LDIF_FILE.get());
088    
089        argDisplayMap.put(
090                ATTR_IMPORT_APPEND,
091                INFO_IMPORT_ARG_APPEND.get());
092    
093        argDisplayMap.put(
094                ATTR_IMPORT_REPLACE_EXISTING,
095                INFO_IMPORT_ARG_REPLACE_EXISTING.get());
096    
097        argDisplayMap.put(
098                ATTR_IMPORT_BACKEND_ID,
099                INFO_IMPORT_ARG_BACKEND_ID.get());
100    
101        argDisplayMap.put(
102                ATTR_IMPORT_INCLUDE_BRANCH,
103                INFO_IMPORT_ARG_INCL_BRANCH.get());
104    
105        argDisplayMap.put(
106                ATTR_IMPORT_EXCLUDE_BRANCH,
107                INFO_IMPORT_ARG_EXCL_BRANCH.get());
108    
109        argDisplayMap.put(
110                ATTR_IMPORT_INCLUDE_ATTRIBUTE,
111                INFO_IMPORT_ARG_INCL_ATTR.get());
112    
113        argDisplayMap.put(
114                ATTR_IMPORT_EXCLUDE_ATTRIBUTE,
115                INFO_IMPORT_ARG_EXCL_ATTR.get());
116    
117        argDisplayMap.put(
118                ATTR_IMPORT_INCLUDE_FILTER,
119                INFO_IMPORT_ARG_INCL_FILTER.get());
120    
121        argDisplayMap.put(
122                ATTR_IMPORT_EXCLUDE_FILTER,
123                INFO_IMPORT_ARG_EXCL_FILTER.get());
124    
125        argDisplayMap.put(
126                ATTR_IMPORT_REJECT_FILE,
127                INFO_IMPORT_ARG_REJECT_FILE.get());
128    
129        argDisplayMap.put(
130                ATTR_IMPORT_SKIP_FILE,
131                INFO_IMPORT_ARG_SKIP_FILE.get());
132    
133        argDisplayMap.put(
134                ATTR_IMPORT_OVERWRITE,
135                INFO_IMPORT_ARG_OVERWRITE.get());
136    
137        argDisplayMap.put(
138                ATTR_IMPORT_SKIP_SCHEMA_VALIDATION,
139                INFO_IMPORT_ARG_SKIP_SCHEMA_VALIDATION.get());
140    
141        argDisplayMap.put(
142                ATTR_IMPORT_IS_COMPRESSED,
143                INFO_IMPORT_ARG_IS_COMPRESSED.get());
144    
145        argDisplayMap.put(
146                ATTR_IMPORT_IS_ENCRYPTED,
147                INFO_IMPORT_ARG_IS_ENCRYPTED.get());
148    
149        argDisplayMap.put(
150                ATTR_IMPORT_CLEAR_BACKEND,
151                INFO_IMPORT_ARG_CLEAR_BACKEND.get());
152      }
153    
154    
155      boolean append                  = false;
156      boolean isCompressed            = false;
157      boolean isEncrypted             = false;
158      boolean overwrite               = false;
159      boolean replaceExisting         = false;
160      boolean skipSchemaValidation    = false;
161      boolean clearBackend            = false;
162      String  backendID               = null;
163      String  rejectFile              = null;
164      String  skipFile                = null;
165      ArrayList<String>  excludeAttributeStrings = null;
166      ArrayList<String>  excludeBranchStrings    = null;
167      ArrayList<String>  excludeFilterStrings    = null;
168      ArrayList<String>  includeAttributeStrings = null;
169      ArrayList<String>  includeBranchStrings    = null;
170      ArrayList<String>  includeFilterStrings    = null;
171      ArrayList<String>  ldifFiles               = null;
172    
173      private LDIFImportConfig importConfig;
174    
175      /**
176       * {@inheritDoc}
177       */
178      public Message getDisplayName() {
179        return INFO_TASK_IMPORT_NAME.get();
180      }
181    
182      /**
183       * {@inheritDoc}
184       */
185      public Message getAttributeDisplayName(String name) {
186        return argDisplayMap.get(name);
187      }
188    
189      /**
190       * {@inheritDoc}
191       */
192      @Override public void initializeTask() throws DirectoryException
193      {
194        // If the client connection is available, then make sure the associated
195        // client has the LDIF_IMPORT privilege.
196        Operation operation = getOperation();
197        if (operation != null)
198        {
199          ClientConnection clientConnection = operation.getClientConnection();
200          if (! clientConnection.hasPrivilege(Privilege.LDIF_IMPORT, operation))
201          {
202            Message message = ERR_TASK_LDIFIMPORT_INSUFFICIENT_PRIVILEGES.get();
203            throw new DirectoryException(ResultCode.INSUFFICIENT_ACCESS_RIGHTS,
204                                         message);
205          }
206        }
207    
208    
209        Entry taskEntry = getTaskEntry();
210    
211        AttributeType typeLdifFile;
212        AttributeType typeAppend;
213        AttributeType typeReplaceExisting;
214        AttributeType typeBackendID;
215        AttributeType typeIncludeBranch;
216        AttributeType typeExcludeBranch;
217        AttributeType typeIncludeAttribute;
218        AttributeType typeExcludeAttribute;
219        AttributeType typeIncludeFilter;
220        AttributeType typeExcludeFilter;
221        AttributeType typeRejectFile;
222        AttributeType typeSkipFile;
223        AttributeType typeOverwrite;
224        AttributeType typeSkipSchemaValidation;
225        AttributeType typeIsCompressed;
226        AttributeType typeIsEncrypted;
227        AttributeType typeClearBackend;
228    
229        typeLdifFile =
230             getAttributeType(ATTR_IMPORT_LDIF_FILE, true);
231        typeAppend =
232             getAttributeType(ATTR_IMPORT_APPEND, true);
233        typeReplaceExisting =
234             getAttributeType(ATTR_IMPORT_REPLACE_EXISTING, true);
235        typeBackendID =
236             getAttributeType(ATTR_IMPORT_BACKEND_ID, true);
237        typeIncludeBranch =
238             getAttributeType(ATTR_IMPORT_INCLUDE_BRANCH, true);
239        typeExcludeBranch =
240             getAttributeType(ATTR_IMPORT_EXCLUDE_BRANCH, true);
241        typeIncludeAttribute =
242             getAttributeType(ATTR_IMPORT_INCLUDE_ATTRIBUTE, true);
243        typeExcludeAttribute =
244             getAttributeType(ATTR_IMPORT_EXCLUDE_ATTRIBUTE, true);
245        typeIncludeFilter =
246             getAttributeType(ATTR_IMPORT_INCLUDE_FILTER, true);
247        typeExcludeFilter =
248             getAttributeType(ATTR_IMPORT_EXCLUDE_FILTER, true);
249        typeRejectFile =
250             getAttributeType(ATTR_IMPORT_REJECT_FILE, true);
251        typeSkipFile =
252          getAttributeType(ATTR_IMPORT_SKIP_FILE, true);
253        typeOverwrite =
254             getAttributeType(ATTR_IMPORT_OVERWRITE, true);
255        typeSkipSchemaValidation =
256             getAttributeType(ATTR_IMPORT_SKIP_SCHEMA_VALIDATION, true);
257        typeIsCompressed =
258             getAttributeType(ATTR_IMPORT_IS_COMPRESSED, true);
259        typeIsEncrypted =
260             getAttributeType(ATTR_IMPORT_IS_ENCRYPTED, true);
261        typeClearBackend =
262             getAttributeType(ATTR_IMPORT_CLEAR_BACKEND, true);
263    
264        List<Attribute> attrList;
265    
266        attrList = taskEntry.getAttribute(typeLdifFile);
267        ldifFiles = TaskUtils.getMultiValueString(attrList);
268    
269        attrList = taskEntry.getAttribute(typeAppend);
270        append = TaskUtils.getBoolean(attrList, false);
271    
272        attrList = taskEntry.getAttribute(typeReplaceExisting);
273        replaceExisting = TaskUtils.getBoolean(attrList, false);
274    
275        attrList = taskEntry.getAttribute(typeBackendID);
276        backendID = TaskUtils.getSingleValueString(attrList);
277    
278        attrList = taskEntry.getAttribute(typeIncludeBranch);
279        includeBranchStrings = TaskUtils.getMultiValueString(attrList);
280    
281        attrList = taskEntry.getAttribute(typeExcludeBranch);
282        excludeBranchStrings = TaskUtils.getMultiValueString(attrList);
283    
284        attrList = taskEntry.getAttribute(typeIncludeAttribute);
285        includeAttributeStrings = TaskUtils.getMultiValueString(attrList);
286    
287        attrList = taskEntry.getAttribute(typeExcludeAttribute);
288        excludeAttributeStrings = TaskUtils.getMultiValueString(attrList);
289    
290        attrList = taskEntry.getAttribute(typeIncludeFilter);
291        includeFilterStrings = TaskUtils.getMultiValueString(attrList);
292    
293        attrList = taskEntry.getAttribute(typeExcludeFilter);
294        excludeFilterStrings = TaskUtils.getMultiValueString(attrList);
295    
296        attrList = taskEntry.getAttribute(typeRejectFile);
297        rejectFile = TaskUtils.getSingleValueString(attrList);
298    
299        attrList = taskEntry.getAttribute(typeSkipFile);
300        skipFile = TaskUtils.getSingleValueString(attrList);
301    
302        attrList = taskEntry.getAttribute(typeOverwrite);
303        overwrite = TaskUtils.getBoolean(attrList, false);
304    
305        attrList = taskEntry.getAttribute(typeSkipSchemaValidation);
306        skipSchemaValidation = TaskUtils.getBoolean(attrList, false);
307    
308        attrList = taskEntry.getAttribute(typeIsCompressed);
309        isCompressed = TaskUtils.getBoolean(attrList, false);
310    
311        attrList = taskEntry.getAttribute(typeIsEncrypted);
312        isEncrypted = TaskUtils.getBoolean(attrList, false);
313    
314        attrList = taskEntry.getAttribute(typeClearBackend);
315        clearBackend = TaskUtils.getBoolean(attrList, false);
316    
317        // Make sure that either the "includeBranchStrings" argument or the
318        // "backendID" argument was provided.
319        if(includeBranchStrings.isEmpty() && backendID == null)
320        {
321          Message message = ERR_LDIFIMPORT_MISSING_BACKEND_ARGUMENT.get(
322              typeIncludeBranch.getNameOrOID(), typeBackendID.getNameOrOID());
323          throw new DirectoryException(ResultCode.UNWILLING_TO_PERFORM, message);
324        }
325    
326        Backend backend = null;
327        ArrayList<DN> defaultIncludeBranches;
328        ArrayList<DN> excludeBranches =
329            new ArrayList<DN>(excludeBranchStrings.size());
330        ArrayList<DN> includeBranches =
331            new ArrayList<DN>(includeBranchStrings.size());
332    
333        for (String s : includeBranchStrings)
334        {
335          DN includeBranch;
336          try
337          {
338            includeBranch = DN.decode(s);
339          }
340          catch (DirectoryException de)
341          {
342            Message message = ERR_LDIFIMPORT_CANNOT_DECODE_INCLUDE_BASE.get(
343                s, de.getMessageObject());
344            throw new DirectoryException(ResultCode.UNWILLING_TO_PERFORM, message);
345          }
346          catch (Exception e)
347          {
348            Message message = ERR_LDIFIMPORT_CANNOT_DECODE_INCLUDE_BASE.get(
349                s, getExceptionMessage(e));
350            throw new DirectoryException(ResultCode.UNWILLING_TO_PERFORM, message);
351          }
352    
353          if(! includeBranches.contains(includeBranch))
354          {
355            includeBranches.add(includeBranch);
356          }
357        }
358        for (String s : excludeBranchStrings)
359        {
360          DN excludeBranch;
361          try
362          {
363            excludeBranch = DN.decode(s);
364          }
365          catch (DirectoryException de)
366          {
367            Message message = ERR_LDIFIMPORT_CANNOT_DECODE_EXCLUDE_BASE.get(
368                s, de.getMessageObject());
369            throw new DirectoryException(ResultCode.UNWILLING_TO_PERFORM, message);
370          }
371          catch (Exception e)
372          {
373            Message message = ERR_LDIFIMPORT_CANNOT_DECODE_EXCLUDE_BASE.get(
374                s, getExceptionMessage(e));
375            throw new DirectoryException(ResultCode.UNWILLING_TO_PERFORM, message);
376          }
377    
378          if (! excludeBranches.contains(excludeBranch))
379          {
380            excludeBranches.add(excludeBranch);
381          }
382        }
383    
384        for (String filterString : excludeFilterStrings)
385        {
386          try
387          {
388            SearchFilter.createFilterFromString(filterString);
389          }
390          catch (DirectoryException de)
391          {
392            Message message = ERR_LDIFIMPORT_CANNOT_PARSE_EXCLUDE_FILTER.get(
393                filterString, de.getMessageObject());
394            throw new DirectoryException(ResultCode.UNWILLING_TO_PERFORM, message);
395          }
396        }
397    
398        for (String filterString : includeFilterStrings)
399        {
400          try
401          {
402            SearchFilter.createFilterFromString(filterString);
403          }
404          catch (DirectoryException de)
405          {
406            Message message = ERR_LDIFIMPORT_CANNOT_PARSE_INCLUDE_FILTER.get(
407                filterString, de.getMessageObject());
408            throw new DirectoryException(ResultCode.UNWILLING_TO_PERFORM, message);
409          }
410        }
411    
412        if(backendID != null)
413        {
414          backend = DirectoryServer.getBackend(backendID);
415          if (backend == null)
416          {
417            Message message = ERR_LDIFIMPORT_NO_BACKENDS_FOR_ID.get();
418            throw new DirectoryException(ResultCode.UNWILLING_TO_PERFORM, message);
419          }
420          else if (! backend.supportsLDIFImport())
421          {
422            Message message = ERR_LDIFIMPORT_CANNOT_IMPORT.get(backendID);
423            throw new DirectoryException(ResultCode.UNWILLING_TO_PERFORM, message);
424          }
425          // Make sure that if the "backendID" argument was provided, no include
426          // base was included, and the "append" ption was not provided, the
427          // "clearBackend" argument was also provided if there are more then one
428          // baseDNs for the backend being imported.
429          else if(!append && includeBranchStrings.isEmpty() &&
430              backend.getBaseDNs().length > 1 && !clearBackend)
431          {
432            StringBuilder builder = new StringBuilder();
433            for(DN dn : backend.getBaseDNs())
434            {
435              builder.append(dn.toNormalizedString());
436              builder.append(" ");
437            }
438            Message message = ERR_LDIFIMPORT_MISSING_CLEAR_BACKEND.get(
439                builder.toString(), typeClearBackend.getNameOrOID());
440            throw new DirectoryException(ResultCode.UNWILLING_TO_PERFORM, message);
441          }
442        }
443        else
444        {
445          // Find the backend that includes all the branches.
446          for(DN includeBranch : includeBranches)
447          {
448            Backend locatedBackend = DirectoryServer.getBackend(includeBranch);
449            if(locatedBackend != null)
450            {
451              if(backend == null)
452              {
453                backend = locatedBackend;
454              }
455              else if(backend != locatedBackend)
456              {
457                // The include branches span across multiple backends.
458                Message message = ERR_LDIFIMPORT_INVALID_INCLUDE_BASE.get(
459                    includeBranch.toNormalizedString(), backend.getBackendID());
460                throw new DirectoryException(ResultCode.UNWILLING_TO_PERFORM,
461                                             message);
462              }
463            }
464          }
465        }
466    
467        // Make sure the selected backend will handle all the include branches
468        defaultIncludeBranches = new ArrayList<DN>(backend.getBaseDNs().length);
469        for (DN dn : backend.getBaseDNs())
470        {
471          defaultIncludeBranches.add(dn);
472        }
473    
474        for(DN includeBranch : includeBranches)
475        {
476          if (! Backend.handlesEntry(includeBranch, defaultIncludeBranches,
477                                     excludeBranches))
478          {
479            Message message = ERR_LDIFIMPORT_INVALID_INCLUDE_BASE.get(
480                includeBranch.toNormalizedString(), backend.getBackendID());
481            throw new DirectoryException(ResultCode.UNWILLING_TO_PERFORM, message);
482          }
483        }
484      }
485    
486    
487      /**
488       * {@inheritDoc}
489       */
490      public void interruptTask(TaskState interruptState, Message interruptReason)
491      {
492        if (TaskState.STOPPED_BY_ADMINISTRATOR.equals(interruptState) &&
493                importConfig != null)
494        {
495          addLogMessage(TaskMessages.INFO_TASK_STOPPED_BY_ADMIN.get(
496                  interruptReason));
497          setTaskInterruptState(interruptState);
498          importConfig.cancel();
499        }
500      }
501    
502    
503      /**
504       * {@inheritDoc}
505       */
506      public boolean isInterruptable()
507      {
508        return true;
509      }
510    
511    
512      /**
513       * {@inheritDoc}
514       */
515      protected TaskState runTask()
516      {
517        // See if there were any user-defined sets of include/exclude attributes or
518        // filters.  If so, then process them.
519        HashSet<AttributeType> excludeAttributes =
520             new HashSet<AttributeType>(excludeAttributeStrings.size());
521        for (String attrName : excludeAttributeStrings)
522        {
523          String        lowerName = attrName.toLowerCase();
524          AttributeType attrType  = DirectoryServer.getAttributeType(lowerName);
525          if (attrType == null)
526          {
527            attrType = DirectoryServer.getDefaultAttributeType(attrName);
528          }
529    
530          excludeAttributes.add(attrType);
531        }
532    
533        HashSet<AttributeType> includeAttributes =
534             new HashSet<AttributeType>(includeAttributeStrings.size());
535        for (String attrName : includeAttributeStrings)
536        {
537          String        lowerName = attrName.toLowerCase();
538          AttributeType attrType  = DirectoryServer.getAttributeType(lowerName);
539          if (attrType == null)
540          {
541            attrType = DirectoryServer.getDefaultAttributeType(attrName);
542          }
543    
544          includeAttributes.add(attrType);
545        }
546    
547        ArrayList<SearchFilter> excludeFilters =
548             new ArrayList<SearchFilter>(excludeFilterStrings.size());
549        for (String filterString : excludeFilterStrings)
550        {
551          try
552          {
553            excludeFilters.add(SearchFilter.createFilterFromString(filterString));
554          }
555          catch (DirectoryException de)
556          {
557            Message message = ERR_LDIFIMPORT_CANNOT_PARSE_EXCLUDE_FILTER.get(
558                filterString, de.getMessageObject());
559            logError(message);
560            return TaskState.STOPPED_BY_ERROR;
561          }
562        }
563    
564        ArrayList<SearchFilter> includeFilters =
565             new ArrayList<SearchFilter>(includeFilterStrings.size());
566        for (String filterString : includeFilterStrings)
567        {
568          try
569          {
570            includeFilters.add(SearchFilter.createFilterFromString(filterString));
571          }
572          catch (DirectoryException de)
573          {
574            Message message = ERR_LDIFIMPORT_CANNOT_PARSE_INCLUDE_FILTER.get(
575                filterString, de.getMessageObject());
576            logError(message);
577            return TaskState.STOPPED_BY_ERROR;
578          }
579        }
580    
581    
582        // Get the backend into which the LDIF should be imported.
583        Backend       backend = null;
584        ArrayList<DN> defaultIncludeBranches;
585        ArrayList<DN> excludeBranches =
586            new ArrayList<DN>(excludeBranchStrings.size());
587        ArrayList<DN> includeBranches =
588            new ArrayList<DN>(includeBranchStrings.size());
589    
590        for (String s : includeBranchStrings)
591        {
592          DN includeBranch;
593          try
594          {
595            includeBranch = DN.decode(s);
596          }
597          catch (DirectoryException de)
598          {
599            Message message = ERR_LDIFIMPORT_CANNOT_DECODE_INCLUDE_BASE.get(
600                s, de.getMessageObject());
601            logError(message);
602            return TaskState.STOPPED_BY_ERROR;
603          }
604          catch (Exception e)
605          {
606            Message message = ERR_LDIFIMPORT_CANNOT_DECODE_INCLUDE_BASE.get(
607                s, getExceptionMessage(e));
608            logError(message);
609            return TaskState.STOPPED_BY_ERROR;
610          }
611    
612          if(! includeBranches.contains(includeBranch))
613          {
614            includeBranches.add(includeBranch);
615          }
616        }
617    
618        if(backendID != null)
619        {
620          backend = DirectoryServer.getBackend(backendID);
621    
622          if (backend == null)
623          {
624            Message message = ERR_LDIFIMPORT_NO_BACKENDS_FOR_ID.get();
625            logError(message);
626            return TaskState.STOPPED_BY_ERROR;
627          }
628          else if (! backend.supportsLDIFImport())
629          {
630            Message message = ERR_LDIFIMPORT_CANNOT_IMPORT.get(backendID);
631            logError(message);
632            return TaskState.STOPPED_BY_ERROR;
633          }
634          // Make sure that if the "backendID" argument was provided, no include
635          // base was included, and the "append" ption was not provided, the
636          // "clearBackend" argument was also provided if there are more then one
637          // baseDNs for the backend being imported.
638          else if(!append && includeBranches.isEmpty() &&
639              backend.getBaseDNs().length > 1 && !clearBackend)
640          {
641            StringBuilder builder = new StringBuilder();
642            builder.append(backend.getBaseDNs()[0].toNormalizedString());
643            for(int i = 1; i < backend.getBaseDNs().length; i++)
644            {
645              builder.append(" / ");
646              builder.append(backend.getBaseDNs()[i].toNormalizedString());
647            }
648            Message message = ERR_LDIFIMPORT_MISSING_CLEAR_BACKEND.get(
649                builder.toString(), ATTR_IMPORT_CLEAR_BACKEND);
650            logError(message);
651            return TaskState.STOPPED_BY_ERROR;
652          }
653        }
654        else
655        {
656          // Find the backend that includes all the branches.
657          for(DN includeBranch : includeBranches)
658          {
659            Backend locatedBackend = DirectoryServer.getBackend(includeBranch);
660            if(locatedBackend != null)
661            {
662              if(backend == null)
663              {
664                backend = locatedBackend;
665              }
666              else if(backend != locatedBackend)
667              {
668                // The include branches span across multiple backends.
669                Message message = ERR_LDIFIMPORT_INVALID_INCLUDE_BASE.get(
670                    includeBranch.toNormalizedString(), backend.getBackendID());
671                logError(message);
672                return TaskState.STOPPED_BY_ERROR;
673              }
674            }
675          }
676        }
677    
678        // Find backends with subordinate base DNs that should be excluded from the
679        // import.
680    
681        defaultIncludeBranches = new ArrayList<DN>(backend.getBaseDNs().length);
682        for (DN dn : backend.getBaseDNs())
683        {
684          defaultIncludeBranches.add(dn);
685        }
686    
687        if (backend.getSubordinateBackends() != null)
688        {
689          for (Backend subBackend : backend.getSubordinateBackends())
690          {
691            for (DN baseDN : subBackend.getBaseDNs())
692            {
693              for (DN importBase : defaultIncludeBranches)
694              {
695                if (baseDN.isDescendantOf(importBase) &&
696                     (! baseDN.equals(importBase)))
697                {
698                  if (! excludeBranches.contains(baseDN))
699                  {
700                    excludeBranches.add(baseDN);
701                  }
702    
703                  break;
704                }
705              }
706            }
707          }
708        }
709    
710        for (String s : excludeBranchStrings)
711        {
712          DN excludeBranch;
713          try
714          {
715            excludeBranch = DN.decode(s);
716          }
717          catch (DirectoryException de)
718          {
719            Message message = ERR_LDIFIMPORT_CANNOT_DECODE_EXCLUDE_BASE.get(
720                s, de.getMessageObject());
721            logError(message);
722            return TaskState.STOPPED_BY_ERROR;
723          }
724          catch (Exception e)
725          {
726            Message message = ERR_LDIFIMPORT_CANNOT_DECODE_EXCLUDE_BASE.get(
727                s, getExceptionMessage(e));
728            logError(message);
729            return TaskState.STOPPED_BY_ERROR;
730          }
731    
732          if (! excludeBranches.contains(excludeBranch))
733          {
734            excludeBranches.add(excludeBranch);
735          }
736        }
737    
738        if (includeBranchStrings.isEmpty())
739        {
740          includeBranches = defaultIncludeBranches;
741        }
742        else
743        {
744          // Make sure the selected backend will handle all the include branches
745          for(DN includeBranch : includeBranches)
746          {
747            if (! Backend.handlesEntry(includeBranch, defaultIncludeBranches,
748                                       excludeBranches))
749            {
750              Message message = ERR_LDIFIMPORT_INVALID_INCLUDE_BASE.get(
751                  includeBranch.toNormalizedString(), backend.getBackendID());
752              logError(message);
753              return TaskState.STOPPED_BY_ERROR;
754            }
755          }
756        }
757    
758        // Create the LDIF import configuration to use when reading the LDIF.
759        ArrayList<String> fileList = new ArrayList<String>(ldifFiles);
760        importConfig = new LDIFImportConfig(fileList);
761        importConfig.setAppendToExistingData(append);
762        importConfig.setReplaceExistingEntries(replaceExisting);
763        importConfig.setCompressed(isCompressed);
764        importConfig.setEncrypted(isEncrypted);
765        importConfig.setClearBackend(clearBackend);
766        importConfig.setExcludeAttributes(excludeAttributes);
767        importConfig.setExcludeBranches(excludeBranches);
768        importConfig.setExcludeFilters(excludeFilters);
769        importConfig.setIncludeAttributes(includeAttributes);
770        importConfig.setIncludeBranches(includeBranches);
771        importConfig.setIncludeFilters(includeFilters);
772        importConfig.setValidateSchema(!skipSchemaValidation);
773    
774        // FIXME -- Should this be conditional?
775        importConfig.setInvokeImportPlugins(true);
776    
777        if (rejectFile != null)
778        {
779          try
780          {
781            ExistingFileBehavior existingBehavior;
782            if (overwrite)
783            {
784              existingBehavior = ExistingFileBehavior.OVERWRITE;
785            }
786            else
787            {
788              existingBehavior = ExistingFileBehavior.APPEND;
789            }
790    
791            importConfig.writeRejectedEntries(rejectFile, existingBehavior);
792          }
793          catch (Exception e)
794          {
795            Message message = ERR_LDIFIMPORT_CANNOT_OPEN_REJECTS_FILE.get(
796                rejectFile, getExceptionMessage(e));
797            logError(message);
798            return TaskState.STOPPED_BY_ERROR;
799          }
800        }
801    
802        if (skipFile != null)
803        {
804          try
805          {
806            ExistingFileBehavior existingBehavior;
807            if (overwrite)
808            {
809              existingBehavior = ExistingFileBehavior.OVERWRITE;
810            }
811            else
812            {
813              existingBehavior = ExistingFileBehavior.APPEND;
814            }
815    
816            importConfig.writeRejectedEntries(skipFile, existingBehavior);
817          }
818          catch (Exception e)
819          {
820            Message message = ERR_LDIFIMPORT_CANNOT_OPEN_SKIP_FILE.get(
821                skipFile, getExceptionMessage(e));
822            logError(message);
823            return TaskState.STOPPED_BY_ERROR;
824          }
825        }
826    
827        // Get the set of base DNs for the backend as an array.
828        DN[] baseDNs = new DN[defaultIncludeBranches.size()];
829        defaultIncludeBranches.toArray(baseDNs);
830    
831        // Notify the task listeners that an import is going to start
832        // this must be done before disabling the backend to allow
833        // listeners to get access to the backend configuration
834        // and to take appropriate actions.
835        DirectoryServer.notifyImportBeginning(backend, importConfig);
836    
837        // Disable the backend.
838        try
839        {
840          TaskUtils.disableBackend(backend.getBackendID());
841        }
842        catch (DirectoryException e)
843        {
844          if (debugEnabled())
845          {
846            TRACER.debugCaught(DebugLogLevel.ERROR, e);
847          }
848    
849          logError(e.getMessageObject());
850          return TaskState.STOPPED_BY_ERROR;
851        }
852    
853    
854        try
855        {
856          // Acquire an exclusive lock for the backend.
857          try
858          {
859            String lockFile = LockFileManager.getBackendLockFileName(backend);
860            StringBuilder failureReason = new StringBuilder();
861            if (! LockFileManager.acquireExclusiveLock(lockFile, failureReason))
862            {
863              Message message = ERR_LDIFIMPORT_CANNOT_LOCK_BACKEND.get(
864                  backend.getBackendID(), String.valueOf(failureReason));
865              logError(message);
866              return TaskState.STOPPED_BY_ERROR;
867            }
868          }
869          catch (Exception e)
870          {
871            if (debugEnabled())
872            {
873              TRACER.debugCaught(DebugLogLevel.ERROR, e);
874            }
875    
876            Message message = ERR_LDIFIMPORT_CANNOT_LOCK_BACKEND.get(
877                backend.getBackendID(), getExceptionMessage(e));
878            logError(message);
879            return TaskState.STOPPED_BY_ERROR;
880          }
881    
882    
883          // Launch the import.
884          try
885          {
886            backend.importLDIF(importConfig);
887          }
888          catch (DirectoryException de)
889          {
890            if (debugEnabled())
891            {
892              TRACER.debugCaught(DebugLogLevel.ERROR, de);
893            }
894    
895            DirectoryServer.notifyImportEnded(backend, importConfig, false);
896            Message message =
897                ERR_LDIFIMPORT_ERROR_DURING_IMPORT.get(de.getMessageObject());
898            logError(message);
899            return TaskState.STOPPED_BY_ERROR;
900          }
901          catch (Exception e)
902          {
903            if (debugEnabled())
904            {
905              TRACER.debugCaught(DebugLogLevel.ERROR, e);
906            }
907    
908            DirectoryServer.notifyImportEnded(backend, importConfig, false);
909            Message message =
910                ERR_LDIFIMPORT_ERROR_DURING_IMPORT.get(getExceptionMessage(e));
911            logError(message);
912            return TaskState.STOPPED_BY_ERROR;
913          }
914          finally
915          {
916            // Release the exclusive lock on the backend.
917            try
918            {
919              String lockFile = LockFileManager.getBackendLockFileName(backend);
920              StringBuilder failureReason = new StringBuilder();
921              if (! LockFileManager.releaseLock(lockFile, failureReason))
922              {
923                Message message = WARN_LDIFIMPORT_CANNOT_UNLOCK_BACKEND.get(
924                    backend.getBackendID(), String.valueOf(failureReason));
925                logError(message);
926                return TaskState.COMPLETED_WITH_ERRORS;
927              }
928            }
929            catch (Exception e)
930            {
931              if (debugEnabled())
932              {
933                TRACER.debugCaught(DebugLogLevel.ERROR, e);
934              }
935    
936              Message message = WARN_LDIFIMPORT_CANNOT_UNLOCK_BACKEND.get(
937                  backend.getBackendID(), getExceptionMessage(e));
938              logError(message);
939              return TaskState.COMPLETED_WITH_ERRORS;
940            }
941    
942          }
943        }
944        finally
945        {
946          // Enable the backend.
947          try
948          {
949            TaskUtils.enableBackend(backend.getBackendID());
950            // It is necessary to retrieve the backend structure again
951            // because disabling and enabling it again may have resulted
952            // in a new backend being registered to the server.
953            backend = DirectoryServer.getBackend(backend.getBackendID());
954          }
955          catch (DirectoryException e)
956          {
957            if (debugEnabled())
958            {
959              TRACER.debugCaught(DebugLogLevel.ERROR, e);
960            }
961    
962            logError(e.getMessageObject());
963            return TaskState.STOPPED_BY_ERROR;
964          }
965          DirectoryServer.notifyImportEnded(backend, importConfig, true);
966        }
967    
968    
969        // Clean up after the import by closing the import config.
970        importConfig.close();
971        return getFinalTaskState();
972      }
973    }