summaryrefslogtreecommitdiff
path: root/src/connectors/src/data/io/ldap/LDAPFlatDataWriter.java
diff options
context:
space:
mode:
Diffstat (limited to 'src/connectors/src/data/io/ldap/LDAPFlatDataWriter.java')
-rw-r--r--src/connectors/src/data/io/ldap/LDAPFlatDataWriter.java198
1 files changed, 198 insertions, 0 deletions
diff --git a/src/connectors/src/data/io/ldap/LDAPFlatDataWriter.java b/src/connectors/src/data/io/ldap/LDAPFlatDataWriter.java
new file mode 100644
index 0000000..d1b8918
--- /dev/null
+++ b/src/connectors/src/data/io/ldap/LDAPFlatDataWriter.java
@@ -0,0 +1,198 @@
+/*
+ * SSSync, a Simple and Stupid Synchronizer for data with multi-valued attributes
+ * Copyright (C) 2014 Ludovic Pouzenc <ludovic@pouzenc.fr>
+ *
+ * This file is part of SSSync.
+ *
+ * SSSync is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * SSSync 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 SSSync. If not, see <http://www.gnu.org/licenses/>
+ */
+
+package data.io.ldap;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import com.unboundid.ldap.sdk.Attribute;
+import com.unboundid.ldap.sdk.DN;
+import com.unboundid.ldap.sdk.DeleteRequest;
+import com.unboundid.ldap.sdk.Entry;
+import com.unboundid.ldap.sdk.LDAPConnection;
+import com.unboundid.ldap.sdk.LDAPException;
+import com.unboundid.ldap.sdk.Modification;
+import com.unboundid.ldap.sdk.ModificationType;
+import com.unboundid.ldap.sdk.ModifyRequest;
+import com.unboundid.ldap.sdk.RDN;
+import com.unboundid.ldap.sdk.schema.EntryValidator;
+import com.unboundid.ldif.LDIFException;
+
+import data.MVDataEntry;
+import data.io.AbstractMVDataWriter;
+
+/**
+ * Stream-oriented LDAP writer from a particular LDAP Directory connection.
+ *
+ * @author lpouzenc
+ */
+public class LDAPFlatDataWriter extends AbstractMVDataWriter {
+
+ private final LDAPConnection conn;
+ private final DN baseDN;
+ private final String keyAttr;
+ private final EntryValidator validator;
+
+ /**
+ * Construct a new writer that could insert/update/delete entries on a particular LDAP connection and baseDN.
+ *
+ * @param conn Already initialized LDAP connection where run the search
+ * @param baseDN Search base DN (will return childs of this DN)
+ * @param keyAttr Attribute name that is the primary key of the entry, identifying the entry in a unique manner
+ * @throws LDAPException
+ */
+ public LDAPFlatDataWriter(LDAPConnection conn, String baseDN, String keyAttr) throws LDAPException {
+ this.conn = conn;
+ this.baseDN = new DN(baseDN);
+ this.keyAttr = keyAttr;
+ this.validator = new EntryValidator(conn.getSchema());
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void insert(MVDataEntry newEntry) throws LDAPException {
+ // Build the DN
+ DN dn = new DN(new RDN(keyAttr, newEntry.getKey()), baseDN);
+
+ // Convert storage objects
+ Collection<Attribute> attributes = new ArrayList<Attribute>();
+ for ( Map.Entry<String, String> entry : newEntry.getAllEntries() ) {
+ attributes.add(new Attribute(entry.getKey(), entry.getValue()));
+ }
+ Entry newLDAPEntry = new Entry(dn, attributes);
+
+ // Add the entry
+ if ( dryRun ) {
+ // In dry-run mode, validate the entry
+ ArrayList<String> invalidReasons = new ArrayList<String>(5);
+ boolean valid = validator.entryIsValid(newLDAPEntry, invalidReasons);
+ if ( !valid ) throw new RuntimeException(
+ "Entry validator has failed to verify this entry :\n" + newLDAPEntry.toLDIFString() +
+ "Reasons are :\n" + invalidReasons);
+ } else {
+ // In real-run mode, insert the entry
+ try {
+ conn.add(newLDAPEntry);
+ } catch (LDAPException e) {
+ throw new LDAPException(e.getResultCode(), "Error while inserting this entry :\n" + newLDAPEntry.toLDIFString(), e);
+ }
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void update(MVDataEntry updatedEntry, MVDataEntry originalEntry, Set<String> attrToUpdate) throws LDAPException, LDIFException {
+ // Build the DN
+ DN dn = new DN(new RDN(keyAttr, updatedEntry.getKey()), baseDN);
+
+ // Convert storage objects
+ List<Modification> mods = new ArrayList<Modification>();
+ for ( String attr : attrToUpdate ) {
+ Set<String> originalValues = originalEntry.getValues(attr);
+ Set<String> updatedValues = updatedEntry.getValues(attr);
+
+ Modification modification = null;
+
+ if ( updatedValues.isEmpty() ) {
+ modification = new Modification(ModificationType.DELETE, attr);
+ } else {
+ String[] updatedValuesArr = updatedValues.toArray(new String[0]);
+
+ if ( originalValues.isEmpty() ) {
+ modification = new Modification(ModificationType.ADD, attr, updatedValuesArr);
+ } else {
+ modification = new Modification(ModificationType.REPLACE, attr, updatedValuesArr);
+ }
+ }
+
+ mods.add(modification);
+ }
+ ModifyRequest modReq = new ModifyRequest(dn, mods);
+
+ // Update the entry
+ if ( dryRun ) {
+ // Simulate originalEntry update
+ Collection<Attribute> attributes = new ArrayList<Attribute>();
+ for ( Map.Entry<String, String> entry : originalEntry.getAllEntries() ) {
+ attributes.add(new Attribute(entry.getKey(), entry.getValue()));
+ }
+ Entry originalLDAPEntry = new Entry(dn, attributes);
+
+ // Warning : Unboundid SDK is okay with mandatory attributes with value "" (empty string)
+ // OpenLDAP do not allow that empty strings in mandatory attributes.
+ // Empty strings are discarded by MVDataEntry.put() for now.
+ Entry modifiedLDAPEntry;
+ try {
+ modifiedLDAPEntry = Entry.applyModifications(originalLDAPEntry, false, mods);
+ } catch (LDAPException originalException) {
+ throw new RuntimeException("Entry update simulation has failed while running applyModifications()\n"
+ + "original entry : " + originalEntry + "\n"
+ + "wanted updated entry : " + updatedEntry + "\n"
+ + "modification request : " + modReq,
+ originalException);
+ }
+ ArrayList<String> invalidReasons = new ArrayList<String>(5);
+ boolean valid = validator.entryIsValid(modifiedLDAPEntry, invalidReasons);
+ if ( !valid ) throw new RuntimeException("Entry update simulation has failed while checking entryIsValid()\n"
+ + "modified entry : " + modifiedLDAPEntry.toLDIFString() + "\n"
+ + "reasons :" + invalidReasons);
+ } else {
+ // In real-run mode, update the entry
+ try {
+ conn.modify(modReq);
+ } catch (LDAPException originalException) {
+ throw new LDAPException(originalException.getResultCode(),
+ "Error while updating this entry :\n" + modReq.toLDIFString(),
+ originalException);
+ }
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void delete(MVDataEntry existingEntry) throws LDAPException {
+ // Build the DN
+ DN dn = new DN(new RDN(keyAttr, existingEntry.getKey()), baseDN);
+
+ // Delete the entry
+ try {
+ if ( dryRun ) {
+ //XXX : try to verify the entry existence in dry-run mode ?
+ } else {
+ conn.delete(new DeleteRequest(dn));
+ }
+ } catch (LDAPException originalException) {
+ throw new LDAPException(originalException.getResultCode(),
+ "Error while deleting this dn : " + dn.toString(),
+ originalException);
+ }
+ }
+
+}