Package org.openengsb.infrastructure.ldap

Source Code of org.openengsb.infrastructure.ldap.LdapDao

/**
* Licensed to the Austrian Association for Software Tool Integration (AASTI)
* under one or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information regarding copyright
* ownership. The AASTI licenses this file to you 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.
*/

package org.openengsb.infrastructure.ldap;

import java.util.LinkedList;
import java.util.List;

import org.apache.directory.ldap.client.api.DefaultSchemaLoader;
import org.apache.directory.ldap.client.api.LdapConnection;
import org.apache.directory.ldap.client.api.LdapNetworkConnection;
import org.apache.directory.shared.ldap.model.cursor.EntryCursor;
import org.apache.directory.shared.ldap.model.cursor.SearchCursor;
import org.apache.directory.shared.ldap.model.entry.Entry;
import org.apache.directory.shared.ldap.model.exception.LdapException;
import org.apache.directory.shared.ldap.model.message.AddRequest;
import org.apache.directory.shared.ldap.model.message.AddRequestImpl;
import org.apache.directory.shared.ldap.model.message.BindRequest;
import org.apache.directory.shared.ldap.model.message.BindRequestImpl;
import org.apache.directory.shared.ldap.model.message.DeleteRequest;
import org.apache.directory.shared.ldap.model.message.DeleteRequestImpl;
import org.apache.directory.shared.ldap.model.message.LdapResult;
import org.apache.directory.shared.ldap.model.message.ResultCodeEnum;
import org.apache.directory.shared.ldap.model.message.SearchRequest;
import org.apache.directory.shared.ldap.model.message.SearchRequestImpl;
import org.apache.directory.shared.ldap.model.message.SearchScope;
import org.apache.directory.shared.ldap.model.name.Dn;
import org.openengsb.infrastructure.ldap.model.Node;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
* Dao which provides CRUD methods for Ldap {@link Entry}s.
* */
public class LdapDao {

    private static final Logger LOGGER = LoggerFactory.getLogger(LdapDao.class);

    private LdapConnection connection;

    /**
     * Creates a new LdapDao. Its connection will use the given parameters.
     * */
    public LdapDao(String host, int port) {
        this.connection = new LdapNetworkConnection(host, port);
    }

    /**
     * Constructs a new LdapDao communicating with an Ldap server over given connection.
     * */
    public LdapDao(LdapConnection connection) {
        this.connection = connection;
    }

    public LdapDao() {
    }

    /**
     * Sets the {@link LdapConnection} used by this dao to communicate with an Ldap server.
     * */
    public void setConnection(LdapConnection connection) {
        this.connection = connection;
    }

    /**
     * Returns the {@link LdapConnection} used by this dao to communicate with an Ldap server.
     * */
    public LdapConnection getConnection() {
        return connection;
    }

    /**
     * Opens the dao's connection. Dn and credentials are used for authentication. Throws an {@link LdapDaoException} in
     * case of IO error or invalid dn.
     * */
    public void connect(String dn, String credentials) {
        BindRequest bindRequest = new BindRequestImpl();
        bindRequest.setCredentials(credentials);
        try {
            bindRequest.setDn(new Dn(dn));
            connection.connect();
            connection.bind(bindRequest);
            ((LdapNetworkConnection) connection).loadSchema(new DefaultSchemaLoader(connection));
        } catch (Exception e) {
            throw new LdapDaoException(e);
        }
    }

    /**
     * Closes the dao's connection. {@link LdapDaoException} is thrown in case of IO error.
     * */
    public void disconnect() {
        try {
            connection.unBind();
            connection.close();
        } catch (Exception e) {
            throw new LdapDaoException(e);
        }
    }

    /**
     * Inserts an entry into the DIT. Throws {@link EntryAlreadyExistsException} if an entry with given Dn already
     * exists. Throws {@link MissingParentException} if an ancestor of the entry is missing.
     */
    public void store(Entry entry) throws EntryAlreadyExistsException, MissingParentException {
        AddRequest addRequest = new AddRequestImpl().setEntry(entry);
        LdapResult result;
        try {
            result = connection.add(addRequest).getLdapResult();
        } catch (LdapException e) {
            throw new LdapDaoException(e);
        }
        if (result.getResultCode() == ResultCodeEnum.ENTRY_ALREADY_EXISTS) {
            throw new EntryAlreadyExistsException(entry);
        } else if (result.getResultCode() == ResultCodeEnum.NO_SUCH_OBJECT) {
            throw new MissingParentException(lastMatch(entry.getDn()));
        } else if (result.getResultCode() != ResultCodeEnum.SUCCESS) {
            throw new LdapDaoException(result.getDiagnosticMessage());
        }
    }

    /**
     * Inserts an entry into the DIT. If the entry exists, nothing is done. Throws {@link MissingParentException} if an
     * ancestor of the entry is missing.
     */
    public void storeSkipExisting(Entry entry) throws MissingParentException {
        try {
            store(entry);
        } catch (EntryAlreadyExistsException e) {
            LOGGER.debug("Entry already exists. Skipping.");
        }
    }

    /**
     * Inserts an entry into the DIT. If the entry exists, it is overwritten. Overwriting leaves is straight forward.
     * Overwriting inner nodes will first delete the node's entire subtree, then overwrite the node. Throws
     * {@link MissingParentException} if an ancestor of the entry is missing.
     */
    public void storeOverwriteExisting(Entry entry) throws MissingParentException {
        try {
            deleteSubtreeIncludingRoot(entry.getDn());
        } catch (NoSuchNodeException e) {
            LOGGER.debug("nothing to overwrite");
        } finally {
            try {
                store(entry);
            } catch (EntryAlreadyExistsException e) {
                LOGGER.warn("should never reach here - entry should have been deleted");
            }
        }
    }

    /**
     * Inserts a hierarchy (tree) of entries into the DIT. The hierarchy is passed as a list. The order of the entries
     * in the list is important. Throws {@link MissingParentException} if an ancestor of some entry is missing. In
     * particular this will happen if the list is not ordered properly. If the root entry (=first) already exists, an
     * {@link EntryAlreadyExistsException} is thrown. Note that this action is not atomic.
     */
    public void store(List<Entry> entries) throws EntryAlreadyExistsException, MissingParentException {
        for (Entry e : entries) {
            store(e);
        }
    }

    /**
     * Behaves like {@link #store(List)} except that duplicates do not throw an exception but are collected in a list
     * which will be returned at success. Note that this action is not atomic.
     */
    public List<Entry> storeSkipExisting(List<Entry> entries) throws MissingParentException {
        List<Entry> skippedEntries = new LinkedList<Entry>();
        for (Entry e : entries) {
            try {
                store(e);
            } catch (EntryAlreadyExistsException ex) {
                skippedEntries.add(e);
            }
        }
        return skippedEntries;
    }

    /**
     * Behaves like {@link #store(List)} except that duplicates do not throw an exception but are overwritten as
     * described in {@link #storeOverwriteExisting(Entry)}. Note that this action is not atomic.
     * */
    public void storeOverwriteExisting(List<Entry> entries) throws NoSuchNodeException, MissingParentException {
        for (Entry entry : entries) {
            try {
                store(entry);
            } catch (EntryAlreadyExistsException e) {
                deleteSubtreeIncludingRoot(e.getEntry().getDn());
                try {
                    store(entry);
                } catch (EntryAlreadyExistsException e1) {
                    LOGGER.debug("should never reach here");
                    throw new LdapDaoException(e1);
                }
            }
        }
    }

    /**
     * Returns all entries found exactly one level below parent. Throws {@link NoSuchNodeException} if resolving the
     * argument Dn fails at its leaf. Throws {@link MissingParentException} if resolving fails earlier.
     * */
    public List<Entry> getDirectChildren(Dn parent) throws NoSuchNodeException, MissingParentException {
        return extractEntriesFromCursor(searchOneLevel(parent));
    }

    /**
     * Returns a list of {@link Node}s representing the subtrees of the argument Dn.
     * */
    public List<Node> searchSubtree(Dn parent) throws NoSuchNodeException, MissingParentException {
        SearchCursor cursor = searchOneLevel(parent);
        return extractNodesFromCursor(cursor);
    }

    /**
     * Deletes root and its entire subtree. Throws NoSuchNodeException if root does not exist. Throws
     * MissingParentException if some node above root does not exist.
     * */
    public void deleteSubtreeIncludingRoot(Dn root) throws NoSuchNodeException, MissingParentException {
        deleteSubtreeExcludingRoot(root);
        deleteLeaf(root);
    }

    /**
     * Deletes the entire subtree of root but not root itself. Throws NoSuchNodeException if root does not exist. Throws
     * MissingParentException if some node above root does not exist.
     * */
    public void deleteSubtreeExcludingRoot(Dn root) throws NoSuchNodeException, MissingParentException {
        existsCheck(root);
        try {
            EntryCursor entryCursor = connection.search(root, "(objectclass=*)", SearchScope.ONELEVEL);
            while (entryCursor.next()) {
                deleteSubtreeIncludingRoot(entryCursor.get().getDn());
            }
        } catch (Exception e) {
            throw new LdapDaoException(e);
        }
    }

    /**
     * Returns true if dn exists, false otherwise.
     * */
    public boolean exists(Dn dn) {
        try {
            return connection.exists(dn);
        } catch (LdapException e) {
            throw new LdapDaoException(e);
        }
    }

    /**
     * Returns the {@link Entry} with given {@link Dn}. If dn does not exist but its parent does, a
     * {@link NoSuchNodeException} is thrown. If one of dn's ancestors does not exist, a {@link MissingParentException}
     * is thrown.
     */
    public Entry lookup(Dn dn) throws NoSuchNodeException, MissingParentException {
        existsCheck(dn);
        try {
            return connection.lookup(dn);
        } catch (LdapException e) {
            throw new LdapDaoException(e);
        }
    }

    /**
     * Throws appropriate exceptions for connection.exists(dn).
     * */
    private void existsCheck(Dn dn) throws NoSuchNodeException, MissingParentException {
        try {
            if (!connection.exists(dn.getParent())) {
                throw new MissingParentException(lastMatch(dn));
            } else if (!connection.exists(dn)) {
                throw new NoSuchNodeException(dn);
            }
        } catch (LdapException e) {
            throw new LdapDaoException(e);
        }
    }

    /**
     * Returns a SearchCursor over the direct children of Dn parent. Throws {@link NoSuchNodeException} if resolving the
     * argument Dn fails at its leaf. Throws {@link MissingParentException} if resolving fails earlier.
     * */
    private SearchCursor searchOneLevel(Dn parent) throws NoSuchNodeException, MissingParentException {
        try {
            if (!connection.exists(parent.getParent())) {
                throw new MissingParentException(lastMatch(parent));
            } else if (!connection.exists(parent)) {
                throw new NoSuchNodeException(parent);
            }
        } catch (LdapException e) {
            throw new LdapDaoException(e);
        }
        SearchRequest searchRequest = new SearchRequestImpl();
        searchRequest.setBase(parent);
        searchRequest.setScope(SearchScope.ONELEVEL);
        try {
            searchRequest.setFilter("(objectclass=*)");
            return connection.search(searchRequest);
        } catch (LdapException e) {
            throw new LdapDaoException(e);
        }
    }

    private List<Entry> extractEntriesFromCursor(SearchCursor cursor) {
        List<Entry> result = new LinkedList<Entry>();
        try {
            while (cursor.next()) {
                result.add(cursor.getEntry());
            }
        } catch (Exception e) {
            throw new LdapDaoException(e);
        }
        return result;
    }

    private List<Node> extractNodesFromCursor(SearchCursor cursor) {
        LinkedList<Node> result = new LinkedList<Node>();
        try {
            while (cursor.next()) {
                Node node = new Node(cursor.getEntry());
                result.addFirst(node);
                node.setChildren(searchSubtree(node.getEntry().getDn()));
                for (Node n : node.getChildren()) {
                    n.setParent(node);
                }
            }
        } catch (Exception e) {
            throw new LdapDaoException(e);
        }
        return result;
    }

    private void deleteLeaf(Dn dn) {
        DeleteRequest deleteRequest = new DeleteRequestImpl();
        deleteRequest.setName(dn);
        LdapResult result;
        try {
            result = connection.delete(deleteRequest).getLdapResult();
        } catch (LdapException e) {
            throw new LdapDaoException(e);
        }
        if (result.getResultCode() != ResultCodeEnum.SUCCESS) {
            throw new LdapDaoException(result.getDiagnosticMessage());
        }
    }

    private Dn lastMatch(final Dn dn) throws MissingParentException {
        if (dn == null) {
            throw new MissingParentException((Dn) null);
        }
        try {
            if (connection.exists(dn)) {
                return dn;
            } else {
                return lastMatch(dn.getParent());
            }
        } catch (LdapException e) {
            throw new LdapDaoException(e);
        }
    }

}
TOP

Related Classes of org.openengsb.infrastructure.ldap.LdapDao

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.