/*
* JBoss, Home of Professional Open Source.
* See the COPYRIGHT.txt file distributed with this work for information
* regarding copyright ownership. Some portions may be licensed
* to Red Hat, Inc. under one or more contributor license agreements.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301 USA.
*/
/**
*
* Please see the user's guide for a full description of capabilties, etc.
*
* Description/Assumptions:
* 1. Table's name in source defines the base DN (or context) for the search.
* Example: Table.NameInSource=ou=people,dc=gene,dc=com
* [Optional] The table's name in source can also define a search scope. Append
* a "?" character as a delimiter to the base DN, and add the search scope string.
* The following scopes are available:
* SUBTREE_SCOPE
* ONELEVEL_SCOPE
* OBJECT_SCOPE
* [Default] LDAPConnectorConstants.ldapDefaultSearchScope
* is the default scope used, if no scope is defined (currently, ONELEVEL_SCOPE).
*
* 2. Column's name in source defines the LDAP attribute name.
* [Default] If no name in source is defined, then we attempt to use the column name
* as the LDAP attribute name.
*
*
* TODO: Implement paged searches -- the LDAP server must support VirtualListViews.
* TODO: Implement cancel.
* TODO: Add Sun/Netscape implementation, AD/OpenLDAP implementation.
*
*
* Note:
* Greater than is treated as >=
* Less-than is treater as <=
* If an LDAP entry has more than one entry for an attribute of interest (e.g. a select item), we only return the
* first occurrance. The first occurance is not predictably the same each time, either, according to the LDAP spec.
* If an attribute is not present, we return the empty string. Arguably, we could throw an exception.
*
* Sun LDAP won't support Sort Orders for very large datasets. So, we've set the sorting to NONCRITICAL, which
* allows Sun to ignore the sort order. This will result in the results to come back as unsorted, without any error.
*
* Removed support for ORDER BY for two reasons:
* 1: LDAP appears to have a limit to the number of records that
* can be server-side sorted. When the limit is reached, two things can happen:
* a. If sortControl is set to CRITICAL, then the search fails.
* b. If sortControl is NONCRITICAL, then the search returns, unsorted.
* We'd like to support ORDER BY, no matter how large the size, so we turn it off,
* and allow MetaMatrix to do it for us.
* 2: Supporting ORDER BY appears to negatively effect the query plan
* when cost analysis is used. We stop using dependent queries, and start
* using inner joins.
*
*/
package org.teiid.translator.ldap;
import java.util.ArrayList;
import java.util.List;
import javax.naming.NamingException;
import javax.naming.directory.SearchControls;
import javax.naming.ldap.LdapContext;
import org.teiid.language.Select;
import org.teiid.metadata.Column;
import org.teiid.translator.DataNotAvailableException;
import org.teiid.translator.ExecutionContext;
import org.teiid.translator.ResultSetExecution;
import org.teiid.translator.TranslatorException;
/**
* LDAPSyncQueryExecution is responsible for executing an LDAP search
* corresponding to a read-only "select" query from Teiid.
*/
public class LDAPSyncQueryExecution implements ResultSetExecution {
protected LdapContext ldapConnection;
private Select query;
protected LDAPExecutionFactory executionFactory;
protected ExecutionContext executionContext;
protected LDAPQueryExecution delegate;
/**
* Constructor
* @param executionMode the execution mode.
* @param ctx the execution context.
* @param logger the ConnectorLogger
* @param connection the LDAP Context
*/
public LDAPSyncQueryExecution(Select query, LDAPExecutionFactory factory, ExecutionContext context, LdapContext connection) {
this.ldapConnection = connection;
this.query = query;
this.executionFactory = factory;
this.executionContext = context;
}
/**
* method to execute the supplied query
* @param query the query object.
* @param maxBatchSize the max batch size.
*/
@Override
public void execute() throws TranslatorException {
// Parse the IQuery, and translate it into an appropriate LDAP search.
IQueryToLdapSearchParser parser = new IQueryToLdapSearchParser(this.executionFactory);
LDAPSearchDetails searchDetails = parser.translateSQLQueryToLDAPSearch(query);
// Create and configure the new search context.
LdapContext context = createSearchContext(searchDetails.getContextName());
SearchControls ctrls = setSearchControls(searchDetails);
this.delegate = new LDAPQueryExecution(context, searchDetails, ctrls, this.executionFactory, this.executionContext);
this.delegate.execute();
}
/**
* Perform a lookup against the initial LDAP context, which
* sets the context to something appropriate for the search that is about to occur.
*
*/
protected LdapContext createSearchContext(String contextName) throws TranslatorException {
try {
return (LdapContext) this.ldapConnection.lookup(contextName);
} catch (NamingException ne) {
throw new TranslatorException(LDAPPlugin.Event.TEIID12002, ne, LDAPPlugin.Util.gs(LDAPPlugin.Event.TEIID12002, contextName));
}
}
/**
* Set the search controls
*/
private SearchControls setSearchControls(LDAPSearchDetails searchDetails) {
SearchControls ctrls = new SearchControls();
//ArrayList modelAttrList = searchDetails.getAttributeList();
ArrayList<Column> modelAttrList = searchDetails.getElementList();
String[] attrs = new String[modelAttrList.size()];
for (int i = 0; i < attrs.length; i++) {
attrs[i] = modelAttrList.get(i).getSourceName();
}
ctrls.setSearchScope(searchDetails.getSearchScope());
ctrls.setReturningAttributes(attrs);
long limit = searchDetails.getCountLimit();
if(limit != -1) {
ctrls.setCountLimit(limit);
}
return ctrls;
}
@Override
public List<?> next() throws TranslatorException, DataNotAvailableException {
return this.delegate.next();
}
@Override
public void cancel() throws TranslatorException {
if (this.delegate != null) {
this.delegate.cancel();
}
}
@Override
public void close() {
if (this.delegate != null) {
this.delegate.close();
}
}
// testing
LDAPQueryExecution getDelegate() {
return this.delegate;
}
}