All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.unboundid.ldap.sdk.unboundidds.tools.ParallelUpdateOperationQueue Maven / Gradle / Ivy

Go to download

The UnboundID LDAP SDK for Java is a fast, comprehensive, and easy-to-use Java API for communicating with LDAP directory servers and performing related tasks like reading and writing LDIF, encoding and decoding data using base64 and ASN.1 BER, and performing secure communication. This package contains the Standard Edition of the LDAP SDK, which is a complete, general-purpose library for communicating with LDAPv3 directory servers.

The newest version!
/*
 * Copyright 2008-2024 Ping Identity Corporation
 * All Rights Reserved.
 */
/*
 * Copyright 2008-2024 Ping Identity Corporation
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *    http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
/*
 * Copyright (C) 2008-2024 Ping Identity Corporation
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License (GPLv2 only)
 * or the terms of the GNU Lesser General Public License (LGPLv2.1 only)
 * as published by the Free Software Foundation.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, see .
 */
package com.unboundid.ldap.sdk.unboundidds.tools;



import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedList;

import com.unboundid.ldap.sdk.DN;
import com.unboundid.ldap.sdk.LDAPException;
import com.unboundid.ldif.LDIFChangeRecord;
import com.unboundid.ldif.LDIFDeleteChangeRecord;
import com.unboundid.ldif.LDIFModifyDNChangeRecord;
import com.unboundid.util.Debug;
import com.unboundid.util.NotNull;
import com.unboundid.util.Nullable;



/**
 * This class provides a data structure that will be used to hold the operations
 * to process by the parallel-update tool.  The thread reading change records
 * from the LDIF file will populate this queue, and modify threads will pull
 * entries out.  It will attempt to maintain a sane ordering for the operations
 * in order to resolve any dependencies that may exist between operations.
 * 
*
* NOTE: This class, and other classes within the * {@code com.unboundid.ldap.sdk.unboundidds} package structure, are only * supported for use against Ping Identity, UnboundID, and * Nokia/Alcatel-Lucent 8661 server products. These classes provide support * for proprietary functionality or for external specifications that are not * considered stable or mature enough to be guaranteed to work in an * interoperable way with other types of LDAP servers. *
*/ final class ParallelUpdateOperationQueue { // Indicates whether the end of the LDIF has been reached. private boolean endOfLDIF; // The map that will hold the DNs of the change records currently being // processed. @NotNull private final HashSet activeChanges; // The capacity for this queue. private final int capacity; // The current size of the queue. private int size; // The map that will hold the operations to be processed. @NotNull private final LinkedList opQueue; // The lock that will be used to provide thread safety for the queue. @NotNull private final Object queueLock; // The parallel update instance with which this queue is associated. @NotNull private final ParallelUpdate parallelUpdate; /** * Creates a new instance of this operation queue. * * @param parallelUpdate The parallel update instance with which this queue * is associated. * @param numThreads The number of threads expected to access this * queue. * @param capacity The maximum capacity to use for the queue. */ ParallelUpdateOperationQueue( @NotNull final ParallelUpdate parallelUpdate, final int numThreads, final int capacity) { this.parallelUpdate = parallelUpdate; this.capacity = capacity; endOfLDIF = false; activeChanges = new HashSet<>(numThreads); opQueue = new LinkedList<>(); queueLock = new Object(); size = 0; } /** * Adds the provided LDIF change record to this queue. This method will block * if the queue is currently at its capacity. * * @param changeRecord The change record to be added to this queue. * * @throws InterruptedException If the thread is interrupted while waiting * for available capacity in the queue. */ void addChangeRecord(@NotNull final LDIFChangeRecord changeRecord) throws InterruptedException { // First, make sure that we can parse the DN of the change record. If not, // then reject the change record. try { changeRecord.getParsedDN(); } catch (final LDAPException e) { Debug.debugException(e); parallelUpdate.reject(changeRecord, e); return; } synchronized (queueLock) { while (size >= capacity) { queueLock.wait(1000L); } opQueue.add(changeRecord); size++; queueLock.notifyAll(); } } /** * Retrieves the next LDIF change record to be processed. This method will * block if the queue is currently empty but the end of the LDIF file has not * yet been reached, or if all of the operations currently held in the queue * are dependent upon an operation in progress. * * @param completedDNs The DNs of the entries targeted by the last change. * It should be empty for the first request processed by * a thread, should have a single element if the last * change was an add, delete, or modify, and should have * two elements if the last change was a modify DN. * * @return The next LDIF change record to be processed, or {@code null} if * there are no more change records to process and the end of the * LDIF file has been reached. */ @Nullable() LDIFChangeRecord getChangeRecord(@NotNull final DN... completedDNs) { synchronized (queueLock) { for (final DN dn : completedDNs) { activeChanges.remove(dn); } while (! (endOfLDIF && opQueue.isEmpty())) { if (opQueue.isEmpty()) { try { queueLock.wait(1000L); } catch (final InterruptedException e) { Debug.debugException(e); } continue; } final Iterator iterator = opQueue.iterator(); iteratorLoop: while (iterator.hasNext()) { final LDIFChangeRecord r = iterator.next(); // Get the parsed target DN for the change record. final DN targetDN; try { targetDN = r.getParsedDN(); } catch (final LDAPException e) { // This should never happen, but if it does then reject the change. parallelUpdate.reject(r, e); iterator.remove(); size--; continue; } // Make sure that we are not currently processing any operation that // involves the entry or any of its ancestors. If it is a delete, // then make sure that we are not processing any operation that // involves any of its descendants. If it is a modify DN operation, // then we need to ensure that neither the current DN nor the new DN // conflict with any active operations. if (! activeChanges.isEmpty()) { for (final DN activeDN : activeChanges) { if (activeDN.isAncestorOf(targetDN, true)) { continue iteratorLoop; } } if (r instanceof LDIFDeleteChangeRecord) { for (final DN activeDN : activeChanges) { if (activeDN.isDescendantOf(targetDN, false)) { continue iteratorLoop; } } } else if (r instanceof LDIFModifyDNChangeRecord) { final LDIFModifyDNChangeRecord modDNRecord = (LDIFModifyDNChangeRecord) r; final DN newDN; try { newDN = modDNRecord.getNewDN(); } catch (final LDAPException e) { // We could not parse the new DN, so reject the change. parallelUpdate.reject(r, e); iterator.remove(); size--; continue iteratorLoop; } for (final DN activeDN : activeChanges) { if (activeDN.isDescendantOf(targetDN, false) || activeDN.isAncestorOf(newDN, true) || activeDN.isDescendantOf(newDN, false)) { continue iteratorLoop; } } // At this point, we know that the modify DN will be processed so // reserve the new DN as well. The target DN will be reserved // below. activeChanges.add(newDN); } } // At this point, the change will be processed so remove it from the // queue and add it to the list of active changes. Also, notify any // threads that might be waiting on the queue lock in case // addChangeRecord is waiting on available space. activeChanges.add(targetDN); iterator.remove(); size--; queueLock.notifyAll(); return r; } // If we've gotten here, then the queue isn't empty, but all of the // operations contained in it are dependent upon operations that are // actively being processed. In that case, wait for an operation to // complete before trying again. try { queueLock.wait(1000L); } catch (final InterruptedException e) { Debug.debugException(e); } } } // If we've gotten here, then there is no more data to be processed. return null; } /** * Blocks until the operation queue is idle (i.e., the operation queue is * empty and there are no active changes). */ public void waitUntilIdle() { synchronized (queueLock) { while (true) { if (opQueue.isEmpty() && activeChanges.isEmpty()) { return; } try { queueLock.wait(1000L); } catch (final InterruptedException e) { Debug.debugException(e); } } } } /** * Indicates that the end of the LDIF has been reached and that no more data * will be added to the queue. */ public void setEndOfLDIF() { synchronized (queueLock) { endOfLDIF = true; queueLock.notifyAll(); } } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy