/* * 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; } }