/** * 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.core.services.internal.security.ldap; import java.util.Collections; import java.util.Comparator; import java.util.List; import java.util.UUID; 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.name.Dn; import org.openengsb.infrastructure.ldap.model.Node; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.fasterxml.uuid.EthernetAddress; import com.fasterxml.uuid.Generators; import com.fasterxml.uuid.UUIDComparator; import com.fasterxml.uuid.impl.TimeBasedGenerator; /** * Assists in maintaining order between entries. Many Java collections allow duplicates or return their elements in a * predefined order. Ldap supports no duplicates and does not specify how implementations return their entries. This * class provides the following:<br> * 1) Make non-distinct objects distinct.<br> * 2) Order objects. <br> * Order can only be achieved among distinct objects, so 2) implies 1). * */ public final class TimebasedOrderFilter { private static final Logger LOGGER = LoggerFactory.getLogger(TimebasedOrderFilter.class); public static final String ID_ATTRIBUTE = "org-openengsb-uuid"; public static final String UNIQUE_OBJECT_OC = "org-openengsb-uniqueObject"; private TimebasedOrderFilter() { } /** * Adds a timebased uuid to entry. If updateRdn is true, the uuid becomes the rdn. Use this to handle duplicates. */ public static void addId(Entry entry, boolean updateRdn) { String uuid = newUUID().toString(); try { entry.add(SchemaConstants.OBJECT_CLASS_ATTRIBUTE, UNIQUE_OBJECT_OC); entry.add(ID_ATTRIBUTE, uuid); } catch (LdapException e) { throw new LdapRuntimeException(e); } if (updateRdn) { Dn newDn = LdapUtils.concatDn(ID_ATTRIBUTE, uuid, entry.getDn().getParent()); entry.setDn(newDn); } } /** * Iterates over entries and adds a timebased uuid to each entry. If updateRdn is true, the uuid becomes the rdn. * Use this to handle duplicates. */ public static void addIds(List<Entry> entries, boolean updateRdn) { for (Entry entry : entries) { addId(entry, updateRdn); } } /** * Sorts the list of entries according to their id. This method does not check whether an entry actually has an id * attribute. Non existing attributes are represented as null. The underlying comparator compares null to null as * equal and null to non-null as smaller. * */ public static void sortById(List<Entry> entries) { Collections.sort(entries, new IdComparator()); } /** * Sorts the list of nodes according to the ids of their underlying entries. This method does not check whether an * entry actually has an id attribute. Non existing attributes are represented as null. The underlying comparator * compares null to null as equal and null to non-null as smaller. * */ public static void sortByIdNode(List<Node> nodes) { Collections.sort(nodes, new IdComparatorNode()); } /** * Returns the String value of the id attribute. * */ public static String extractId(Entry entry) { return LdapUtils.extractAttributeNoEmptyCheck(entry, TimebasedOrderFilter.ID_ATTRIBUTE); } private static UUID newUUID() { TimeBasedGenerator uuidGenerator = Generators.timeBasedGenerator(EthernetAddress.fromInterface()); return uuidGenerator.generate(); } private static class IdComparator implements Comparator<Entry> { @Override public int compare(Entry e1, Entry e2) { String id1 = extractId(e1); String id2 = extractId(e2); if (id1 == null && id2 == null) { return 0; } else if (id1 == null && id2 != null) { LOGGER.warn("id2 = emptyString? {}", id2.isEmpty()); return -1; } else if (id1 != null && id2 == null) { LOGGER.warn("id1 = emptyString? {}", id1.isEmpty()); return 1; } else { return UUIDComparator.staticCompare(UUID.fromString(id1), UUID.fromString(id2)); } } } private static class IdComparatorNode implements Comparator<Node> { @Override public int compare(Node n1, Node n2) { Entry e1 = n1.getEntry(); Entry e2 = n2.getEntry(); if (e1 == null && e2 == null) { return 0; } else if (e1 == null && e2 != null) { return -1; } else if (e1 != null && e2 == null) { return 1; } else { return new IdComparator().compare(e1, e2); } } } }