package org.safehaus.penrose.directory;
import org.safehaus.penrose.ldap.DN;
import org.safehaus.penrose.ldap.RDN;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.*;
import java.io.Serializable;
/**
* @author Endi Sukma Dewata
*/
public class DirectoryConfig implements Serializable, Cloneable {
public final static long serialVersionUID = 1L;
public final static List<String> EMPTY_IDS = new ArrayList<String>();
public final static Collection<EntryConfig> EMPTY = new ArrayList<EntryConfig>();
protected List<EntryConfig> entryConfigs = new ArrayList<EntryConfig>();
protected Map<String,EntryConfig> entryConfigByName = new LinkedHashMap<String,EntryConfig>();
protected Map<String,Collection<EntryConfig>> entryConfigsByDn = new LinkedHashMap<String,Collection<EntryConfig>>();
protected Map<String,Collection<EntryConfig>> entryConfigsBySource = new LinkedHashMap<String,Collection<EntryConfig>>();
protected List<String> rootNames = new ArrayList<String>();
protected Map<String,String> parentByName = new LinkedHashMap<String,String>();
protected Map<String,List<String>> childrenByName = new LinkedHashMap<String,List<String>>();
public DirectoryConfig() {
}
public void addEntryConfig(EntryConfig entryConfig) throws Exception {
Logger log = LoggerFactory.getLogger(getClass());
boolean debug = log.isDebugEnabled();
String entryName = entryConfig.getName();
DN dn = entryConfig.getDn();
if (debug) log.debug("Adding entry \""+dn+"\".");
validate(entryConfig);
if (entryConfigByName.containsKey(entryName)) {
throw new Exception("Entry "+entryName+" already exists.");
}
entryConfigs.add(entryConfig);
if (entryName == null) {
int counter = 0;
entryName = "entry"+counter;
while (entryConfigByName.containsKey(entryName)) {
counter++;
entryName = "entry"+counter;
}
entryConfig.setName(entryName);
}
if (debug) log.debug(" - ID: "+ entryName);
// index by entryName
entryConfigByName.put(entryName, entryConfig);
// index by dn
String normalizedDn = dn.getNormalizedDn();
Collection<EntryConfig> c1 = entryConfigsByDn.get(normalizedDn);
if (c1 == null) {
c1 = new LinkedHashSet<EntryConfig>();
entryConfigsByDn.put(normalizedDn, c1);
}
c1.add(entryConfig);
// index by source
for (EntrySourceConfig sourceConfig : entryConfig.getSourceConfigs()) {
String sourceName = sourceConfig.getSourceName();
Collection<EntryConfig> c2 = entryConfigsBySource.get(sourceName);
if (c2 == null) {
c2 = new LinkedHashSet<EntryConfig>();
entryConfigsBySource.put(sourceName, c2);
}
c2.add(entryConfig);
}
String parentName = entryConfig.getParentName();
if (parentName != null) {
if (debug) log.debug(" - Searching parent with name "+parentName);
EntryConfig parent = getEntryConfig(parentName);
if (parent != null) {
if (debug) log.debug(" - Found parent \""+parent.getDn()+"\".");
addChildName(parentName, entryConfig.getName());
return;
}
}
DN parentDn = entryConfig.getParentDn();
if (!parentDn.isEmpty()) {
if (debug) log.debug(" - Searching parent with dn \""+parentDn+"\".");
Collection<EntryConfig> parents = getEntryConfigsByDn(parentDn);
if (!parents.isEmpty()) {
EntryConfig parent = parents.iterator().next();
if (debug) log.debug(" - Found parent \""+parent.getDn()+"\".");
addChildName(parent.getName(), entryConfig.getName());
return;
}
}
if (debug) log.debug(" - Add suffix \""+dn+"\"");
rootNames.add(entryName);
}
public void validate(EntryConfig entryConfig) throws Exception {
String entryName = entryConfig.getName();
if (entryName != null) {
char startingChar = entryName.charAt(0);
if (!Character.isLetter(startingChar)) {
throw new Exception("Invalid service name: "+entryName);
}
for (int i = 1; i<entryName.length(); i++) {
char c = entryName.charAt(i);
if (Character.isLetterOrDigit(c) || c == '_') continue;
throw new Exception("Invalid service name: "+entryName);
}
if (entryConfigByName.containsKey(entryName)) {
throw new Exception("Entry "+entryName+" already exists.");
}
}
}
public boolean contains(EntryConfig entryConfig) {
return entryConfigByName.containsKey(entryConfig.getName());
}
public EntryConfig getEntryConfig(String entryName) {
return entryConfigByName.get(entryName);
}
public String getParentName(String entryName) {
return parentByName.get(entryName);
}
public EntryConfig getParent(EntryConfig entryConfig) {
if (entryConfig == null) return null;
String parentName = parentByName.get(entryConfig.getName());
return entryConfigByName.get(parentName);
}
public void updateEntryConfig(String name, EntryConfig entryConfig) throws Exception {
removeEntryConfig(name);
addEntryConfig(entryConfig);
/*
EntryConfig oldEntryConfig = entryConfigsById.get(name);
DN oldParentDn = oldEntryConfig.getParentDn();
DN parentDn = entryConfig.getParentDn();
if (!oldParentDn.equals(parentDn)) {
throw new Exception("Modify DN operation is not supported.");
}
RDN oldRdn = oldEntryConfig.getRdn();
RDN rdn = entryConfig.getRdn();
if (!oldRdn.equals(rdn)) {
renameEntryConfig(oldEntryConfig, rdn);
}
oldEntryConfig.copy(entryConfig);
*/
}
public void removeEntryConfig(String name) throws Exception {
Logger log = LoggerFactory.getLogger(getClass());
boolean debug = log.isDebugEnabled();
Collection<String> childNames = new ArrayList<String>();
childNames.addAll(getChildNames(name));
for (String childName : childNames) {
removeEntryConfig(childName);
}
EntryConfig entryConfig = entryConfigByName.remove(name);
if (entryConfig == null) return;
entryConfigs.remove(entryConfig);
String parentName = parentByName.get(name);
if (parentName == null) {
if (debug) log.debug("Removing root entry "+name+".");
rootNames.remove(name);
} else {
if (debug) log.debug("Removing entry "+name+" from parent "+parentName+".");
parentByName.remove(name);
List<String> children = childrenByName.get(parentName);
if (children != null) {
children.remove(name);
if (children.isEmpty()) childrenByName.remove(parentName);
}
}
String normalizedDn = entryConfig.getDn().getNormalizedDn();
Collection<EntryConfig> c = entryConfigsByDn.get(normalizedDn);
if (c != null) {
c.remove(entryConfig);
if (c.isEmpty()) entryConfigsByDn.remove(normalizedDn);
}
for (String sourceName : entryConfig.getSourceNames()) {
Collection<EntryConfig> c2 = entryConfigsBySource.get(sourceName);
if (c2 != null) {
c2.remove(entryConfig);
if (c2.isEmpty()) entryConfigsBySource.remove(sourceName);
}
}
}
public Collection<EntryConfig> getEntryConfigs() {
return entryConfigs;
}
public Collection<EntryConfig> getEntryConfigs(Collection<String> names) {
Collection<EntryConfig> entryConfigs = new LinkedHashSet<EntryConfig>();
for (String name : names) {
EntryConfig entryConfig = entryConfigByName.get(name);
if (entryConfig == null) continue;
entryConfigs.add(entryConfig);
}
return entryConfigs;
}
public Collection<String> getEntryNames() {
return entryConfigByName.keySet();
}
public Collection<String> getEntryNames(Collection<EntryConfig> entryConfigs) throws Exception {
Collection<String> names = new LinkedHashSet<String>();
for (EntryConfig entryConfig : entryConfigs) {
names.add(entryConfig.getName());
}
return names;
}
public Collection<String> getEntryNamesByDn(DN dn) throws Exception {
if (dn == null) return EMPTY_IDS;
Collection<EntryConfig> list = entryConfigsByDn.get(dn.getNormalizedDn());
if (list == null) return EMPTY_IDS;
return getEntryNames(list);
}
public String getEntryNameByDn(DN dn) throws Exception {
if (dn == null) return null;
Collection<EntryConfig> list = entryConfigsByDn.get(dn.getNormalizedDn());
if (list == null) return null;
if (list.isEmpty()) return null;
return list.iterator().next().getName();
}
public Collection<String> getEntryNamesBySource(String sourceName) throws Exception {
Collection<EntryConfig> list = entryConfigsBySource.get(sourceName);
if (list == null) return EMPTY_IDS;
return getEntryNames(list);
}
public Collection<EntryConfig> getEntryConfigsBySource(String sourceName) throws Exception {
Collection<EntryConfig> list = entryConfigsBySource.get(sourceName);
if (list == null) return EMPTY;
return list;
}
public Collection<EntryConfig> getEntryConfigsByDn(DN dn) throws Exception {
if (dn == null) return EMPTY;
Collection<EntryConfig> list = entryConfigsByDn.get(dn.getNormalizedDn());
if (list == null) return EMPTY;
return list;
}
public void renameChildren(EntryConfig entryConfig, String newDn) throws Exception {
Logger log = LoggerFactory.getLogger(getClass());
boolean debug = log.isDebugEnabled();
if (entryConfig == null) return;
if (newDn.equals(entryConfig.getDn().toString())) return;
DN oldDn = entryConfig.getDn();
if (debug) log.debug("Renaming "+oldDn+" to "+newDn);
Collection<EntryConfig> c = getEntryConfigsByDn(oldDn);
if (c == null) return;
c.remove(entryConfig);
if (c.isEmpty()) {
if (debug) log.debug("Last "+oldDn);
entryConfigsByDn.remove(oldDn.getNormalizedDn());
}
entryConfig.setStringDn(newDn);
Collection<EntryConfig> newList = entryConfigsByDn.get(newDn.toLowerCase());
if (newList == null) {
if (debug) log.debug("First "+newDn);
newList = new LinkedHashSet<EntryConfig>();
entryConfigsByDn.put(newDn.toLowerCase(), newList);
}
newList.add(entryConfig);
Collection<EntryConfig> children = getChildren(entryConfig);
if (children != null) {
//addChildren(newDn, children);
for (EntryConfig child : children) {
String childNewDn = child.getRdn() + "," + newDn;
//System.out.println(" - renaming child "+child.getDn()+" to "+childNewDn);
renameChildren(child, childNewDn);
}
//removeChildren(oldDn);
}
}
public void renameEntryConfig(EntryConfig entryConfig, DN newDn) throws Exception {
Logger log = LoggerFactory.getLogger(getClass());
boolean debug = log.isDebugEnabled();
EntryConfig oldParent = getParent(entryConfig);
DN oldDn = entryConfig.getDn();
if (debug) log.debug("Renaming "+oldDn+" to "+newDn);
Collection<EntryConfig> c = entryConfigsByDn.get(oldDn.getNormalizedDn());
if (c == null) {
if (debug) log.debug("Entry "+oldDn+" not found.");
return;
}
c.remove(entryConfig);
if (c.isEmpty()) {
if (debug) log.debug("Last "+oldDn);
entryConfigsByDn.remove(oldDn.getNormalizedDn());
}
entryConfig.setDn(newDn);
Collection<EntryConfig> newList = entryConfigsByDn.get(newDn.getNormalizedDn());
if (newList == null) {
if (debug) log.debug("First "+newDn);
newList = new LinkedHashSet<EntryConfig>();
entryConfigsByDn.put(newDn.getNormalizedDn(), newList);
}
newList.add(entryConfig);
EntryConfig newParent = getParent(entryConfig);
if (debug) log.debug("New parent "+(newParent == null ? null : newParent.getDn()));
if (newParent != null) {
addChildName(newParent.getName(), entryConfig.getName());
}
Collection<EntryConfig> children = getChildren(entryConfig);
if (children != null) {
//addChildren(newDn, children);
for (EntryConfig child : children) {
String childNewDn = child.getRdn() + "," + newDn;
//System.out.println(" - renaming child "+child.getDn()+" to "+childNewDn);
renameChildren(child, childNewDn);
}
//removeChildren(oldDn);
}
if (oldParent != null) {
Collection oldSiblings = getChildren(oldParent);
if (oldSiblings != null) oldSiblings.remove(entryConfig);
}
}
public void renameEntryConfig(EntryConfig entryConfig, RDN newRdn) throws Exception {
Logger log = LoggerFactory.getLogger(getClass());
boolean debug = log.isDebugEnabled();
EntryConfig oldParent = getParent(entryConfig);
DN oldDn = entryConfig.getDn();
if (debug) log.debug("Renaming "+oldDn+" to "+newRdn+".");
Collection<EntryConfig> c = entryConfigsByDn.get(oldDn.getNormalizedDn());
if (c == null) {
if (debug) log.debug("Entry "+oldDn+" not found.");
return;
}
c.remove(entryConfig);
if (c.isEmpty()) {
if (debug) log.debug("Last "+oldDn+".");
entryConfigsByDn.remove(oldDn.getNormalizedDn());
}
DN newDn = newRdn.append(oldParent.getDn());
entryConfig.setDn(newDn);
Collection<EntryConfig> newList = entryConfigsByDn.get(newDn.getNormalizedDn());
if (newList == null) {
if (debug) log.debug("First "+newDn+".");
newList = new LinkedHashSet<EntryConfig>();
entryConfigsByDn.put(newDn.getNormalizedDn(), newList);
}
newList.add(entryConfig);
}
public List<String> getChildNames(String entryName) {
if (entryName == null) return rootNames;
List<String> children = childrenByName.get(entryName);
if (children == null) return EMPTY_IDS;
return children;
}
public void setChildNames(String entryName, List<String> childNames) throws Exception {
removeChildNames(entryName);
addChildNames(entryName, childNames);
}
public void addChildName(String entryName, String childName) throws Exception {
Logger log = LoggerFactory.getLogger(getClass());
boolean debug = log.isDebugEnabled();
if (debug) log.debug("Adding child "+childName+" to entry "+entryName+".");
if (entryName == null) {
rootNames.add(childName);
return;
}
List<String> children = childrenByName.get(entryName);
if (children == null) {
children = new ArrayList<String>();
childrenByName.put(entryName, children);
}
children.add(childName);
parentByName.put(childName, entryName);
}
public void addChildNames(String entryName, List<String> childNames) throws Exception {
for (String childName : childNames) {
addChildName(entryName, childName);
}
}
public void removeChildName(String entryName, String childName) throws Exception {
Logger log = LoggerFactory.getLogger(getClass());
boolean debug = log.isDebugEnabled();
if (debug) log.debug("Removing child "+childName+" of entry "+entryName+".");
if (entryName == null) {
rootNames.remove(childName);
return;
}
parentByName.remove(childName);
List<String> children = childrenByName.get(entryName);
if (children == null) return;
children.remove(childName);
if (children.isEmpty()) childrenByName.remove(entryName);
}
public void removeChildNames(String entryName) throws Exception {
if (entryName == null) {
rootNames.clear();
return;
}
List<String> children = childrenByName.get(entryName);
if (children == null) return;
List<String> list = new ArrayList<String>();
list.addAll(children);
for (String childName : list) {
removeChildName(entryName, childName);
}
}
public Collection<EntryConfig> getChildren(EntryConfig parentConfig) {
return getChildren(parentConfig.getName());
}
public Collection<EntryConfig> getChildren(String name) {
List<String> children = childrenByName.get(name);
if (children == null) return EMPTY;
return getEntryConfigs(children);
}
public void removeChildren(EntryConfig parentConfig) {
List<String> names = childrenByName.remove(parentConfig.getName());
for (String name : names) {
parentByName.remove(name);
}
}
public DN getSuffix() {
String name = rootNames.iterator().next();
EntryConfig rootEntry = getEntryConfig(name);
return rootEntry.getDn();
}
public Collection<DN> getSuffixes() {
Collection<DN> list = new LinkedHashSet<DN>();
for (EntryConfig entryConfig : getRootEntryConfigs()) {
DN suffix = entryConfig.getDn();
list.add(suffix);
}
return list;
}
public String getRootName() {
if (rootNames.isEmpty()) return null;
return rootNames.iterator().next();
}
public List<String> getRootNames() {
return rootNames;
}
public Collection<EntryConfig> getRootEntryConfigs() {
return getEntryConfigs(getRootNames());
}
public boolean contains(DN dn) throws Exception {
for (DN suffix : getSuffixes()) {
if (suffix.isEmpty() && dn.isEmpty() // Root DSE
|| dn.endsWith(suffix)) {
return true;
}
}
return false;
}
public Collection<EntryConfig> getEntryConfigs(String dn) throws Exception {
return getEntryConfigsByDn(new DN(dn));
}
public Object clone() throws CloneNotSupportedException {
DirectoryConfig directoryConfig = (DirectoryConfig)super.clone();
directoryConfig.entryConfigs = new ArrayList<EntryConfig>();
directoryConfig.entryConfigByName = new LinkedHashMap<String,EntryConfig>();
directoryConfig.entryConfigsByDn = new LinkedHashMap<String,Collection<EntryConfig>>();
directoryConfig.entryConfigsBySource = new LinkedHashMap<String,Collection<EntryConfig>>();
directoryConfig.rootNames = new ArrayList<String>();
directoryConfig.parentByName = new LinkedHashMap<String,String>();
directoryConfig.childrenByName = new LinkedHashMap<String,List<String>>();
for (EntryConfig entryConfig : entryConfigs) {
try {
directoryConfig.addEntryConfig((EntryConfig) entryConfig.clone());
} catch (Exception e) {
throw new RuntimeException(e.getMessage(), e);
}
}
return directoryConfig;
}
}