package org.nightlabs.jfire.auth.ui.ldap.tree;
import java.util.ArrayList;
import java.util.Collection;
import javax.security.auth.login.LoginException;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Status;
import org.eclipse.core.runtime.jobs.IJobChangeEvent;
import org.eclipse.core.runtime.jobs.JobChangeAdapter;
import org.nightlabs.base.ui.job.Job;
import org.nightlabs.jfire.auth.ui.ldap.LDAPEntrySelectorComposite.BindCredentials;
import org.nightlabs.jfire.auth.ui.ldap.LdapUIPlugin;
import org.nightlabs.jfire.auth.ui.ldap.resource.Messages;
import org.nightlabs.jfire.base.security.integration.ldap.attributes.LDAPAttributeSet;
import org.nightlabs.jfire.base.security.integration.ldap.connection.ILDAPConnectionParamsProvider;
import org.nightlabs.jfire.base.security.integration.ldap.connection.LDAPConnection;
import org.nightlabs.jfire.base.security.integration.ldap.connection.LDAPConnectionManager;
import org.nightlabs.jfire.security.integration.UserManagementSystemCommunicationException;
import org.nightlabs.progress.ProgressMonitor;
/**
* This class represents a node in {@link LDAPTree}. Actual communication with LDAP directory happens here.
*
* @author Denis Dudnik <deniska.dudnik[at]gmail{dot}com>
*
*/
public class LDAPTreeEntry {
/**
* Name of LDAP entry
*/
private String entryName;
/**
* LDAP entry attributes will be loaded if any names are specified. Note that the same attributes will be loaded for all children of this {@link LDAPTreeEntry}.
*/
private String[] attributeNames;
/**
* Loaded LDAP entry attributes. Loading of attributes will occur lazily inside {@link #getAttributes()} method.
* For children of this {@link LDAPTreeEntry} loading will occur inside {@link #getChildren(BindCredentials, LDAPTreeEntryLoadCallback...)} if {@link #attributeNames} are given
*/
private LDAPAttributeSet entryAttributes;
/**
* Connection parameters. They are automatically set to all child entries of this {@link LDAPTreeEntry}.
*/
private ILDAPConnectionParamsProvider ldapConnectionParamsProvider;
/**
* {@link Job} for loading this entry's children
*/
private Job loadJob;
/**
* Loaded children on this {@link LDAPTreeEntry}
*/
private Collection<LDAPTreeEntry> childLdapEntries;
/**
* Constructs new {@link LDAPTreeEntry} with given name
*
* @param entryName
*/
public LDAPTreeEntry(String entryName) {
this(entryName, null);
}
/**
* Constructs new {@link LDAPTreeEntry} with given name and sets attribute names to be loaded
*
* @param entryName
* @param attributeNames
*/
public LDAPTreeEntry(String entryName, String[] attributeNames){
this.entryName = entryName;
this.attributeNames = attributeNames;
}
/**
* If this entry is a root, {@link ILDAPConnectionParamsProvider} should be set. All child entries will get the same
* {@link ILDAPConnectionParamsProvider} automatically when they are loaded. Can't be <code>null</code>.
*
* @param ldapConnectionParamsProvider can't be <code>null</code>, {@link IllegalArgumentException} is thrown otherwise
*/
public void setLdapConnectionParamsProvider(ILDAPConnectionParamsProvider ldapConnectionParamsProvider) {
if (ldapConnectionParamsProvider == null){
throw new IllegalArgumentException("ILDAPConnectionParamsProvider can't be null!"); //$NON-NLS-1$
}
this.ldapConnectionParamsProvider = ldapConnectionParamsProvider;
}
/**
* Get name of this entry
*
* @return Name of this entry
*/
public String getName() {
return entryName;
}
/**
* Get attributes of this {@link LDAPTreeEntry} represented with an {@link LDAPAttributeSet}.
* If attributes are not loaded yet, they will be fetched from LDAP lazily here. Attributes to be
* loaded could be specified by {@link #setAttributeNames(String[])}, all attributes will be loaded otherwise.
*
* As this method could communicate to LDAP directory you should consider to use it within some {@link ProgressMonitor}.
*
* @param bindCredentials Login(entry name) and password for bind, could be <code>null</code> but in this case anonymous access hould be enabled in LDAP directory
* @return {@link LDAPAttributeSet} with attributes of this entry
* @throws UserManagementSystemCommunicationException
* @throws LoginException
*/
public LDAPAttributeSet getAttributes(BindCredentials bindCredentials) throws UserManagementSystemCommunicationException, LoginException {
if (entryAttributes == null){
LDAPConnection connection = null;
try {
connection = LDAPConnectionManager.sharedInstance().getConnection(ldapConnectionParamsProvider);
if (bindCredentials != null){
connection.bind(bindCredentials.getLogin(), bindCredentials.getPassword());
}
entryAttributes = connection.getAttributesForEntry(entryName, attributeNames);
return entryAttributes;
} finally {
LDAPConnectionManager.sharedInstance().releaseConnection(connection);
}
}
return entryAttributes;
}
/**
* Set attribute names to be loaded.
*
* @param attributeNames
*/
public void setAttributeNames(String[] attributeNames) {
this.attributeNames = attributeNames;
}
/**
* Get children of this {@link LDAPTreeEntry}. If they are not loaded, starts a {@link Job} and returns <code>null</code>.
* {@link LDAPTreeEntryLoadCallback}s are called when the {@link Job} is done.
*
* @param bindCredentials Credentials used for binding against LDAP directory if not <code>null</code>
* @param entryLoadCallback Callback(s) to be called after load is done
* @return children of this {@link LDAPTreeEntry} or <code>null</code> if they are not loaded yet
*/
public LDAPTreeEntry[] getChildren(final BindCredentials bindCredentials, final LDAPTreeEntryLoadCallback... entryLoadCallback){
if (childLdapEntries != null){
LDAPTreeEntry[] entries = new LDAPTreeEntry[childLdapEntries.size()];
return childLdapEntries.toArray(entries);
}else{
loadJob = new Job(Messages.getString("org.nightlabs.jfire.auth.ui.ldap.tree.LDAPTreeEntry.loadLDAPEntriesJobTitle")) { //$NON-NLS-1$
@Override
protected IStatus run(ProgressMonitor monitor) throws Exception {
LDAPConnection connection = null;
try {
connection = LDAPConnectionManager.sharedInstance().getConnection(ldapConnectionParamsProvider);
if (bindCredentials != null){
connection.bind(bindCredentials.getLogin(), bindCredentials.getPassword());
}
Collection<String> childEntries = connection.getChildEntries(entryName);
childLdapEntries = new ArrayList<LDAPTreeEntry>();
for (String entryName : childEntries) {
LDAPTreeEntry ldapTreeEntry = new LDAPTreeEntry(entryName, attributeNames);
ldapTreeEntry.setLdapConnectionParamsProvider(ldapConnectionParamsProvider);
ldapTreeEntry.getAttributes(bindCredentials); // loading attributes here to save time later
childLdapEntries.add(ldapTreeEntry);
}
return Status.OK_STATUS;
} catch (Exception e){
return new Status(Status.ERROR, LdapUIPlugin.PLUGIN_ID, Messages.getString("org.nightlabs.jfire.auth.ui.ldap.tree.LDAPTreeEntry.errorStatusTitle"), e); //$NON-NLS-1$
} finally {
LDAPConnectionManager.sharedInstance().releaseConnection(connection);
}
}
};
loadJob.addJobChangeListener(new JobChangeAdapter(){
@Override
public void done(IJobChangeEvent event) {
if (Status.OK_STATUS == event.getResult()
&& entryLoadCallback != null){
for (LDAPTreeEntryLoadCallback ldapTreeEntryLoadedCallback : entryLoadCallback) {
ldapTreeEntryLoadedCallback.treeEntryLoaded(LDAPTreeEntry.this);
}
}else if (Status.ERROR == event.getResult().getSeverity()){
for (LDAPTreeEntryLoadCallback ldapTreeEntryLoadedCallback : entryLoadCallback) {
ldapTreeEntryLoadedCallback.hadleTreeEntryLoadError(event.getResult().getException());
}
}
super.done(event);
}
});
loadJob.schedule();
}
return null;
}
/**
* Check if attributes have been already loaded for this entry.
*
* @return <code>true</code> if attributes were loaded
*/
public boolean hasAttributesLoaded(){
return entryAttributes != null;
}
/**
* Interface for callbacks which are called after load {@link Job} is done.
*
* @author Denis Dudnik <deniska.dudnik[at]gmail{dot}com>
*
*/
public static interface LDAPTreeEntryLoadCallback{
/**
* Called when child entries are loaded successfully by {@link Job} and its return {@link Status} is OK.
*
* @param loadedEntry entry whose children were loaded
*/
void treeEntryLoaded(LDAPTreeEntry loadedEntry);
/**
* Called when some exception was caught during loading process and {@link Job} returned ERROR {@link Status}.
*
* @param cause exception caught during loading process
*/
void hadleTreeEntryLoadError(Throwable cause);
}
}