001: /*
002: * Copyright 2006-2007 The Scriptella Project Team.
003: *
004: * Licensed under the Apache License, Version 2.0 (the "License");
005: * you may not use this file except in compliance with the License.
006: * You may obtain a copy of the License at
007: *
008: * http://www.apache.org/licenses/LICENSE-2.0
009: *
010: * Unless required by applicable law or agreed to in writing, software
011: * distributed under the License is distributed on an "AS IS" BASIS,
012: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013: * See the License for the specific language governing permissions and
014: * limitations under the License.
015: */
016: package scriptella.driver.ldap;
017:
018: import scriptella.expression.PropertiesSubstitutor;
019: import scriptella.spi.ParametersCallback;
020: import scriptella.spi.QueryCallback;
021:
022: import javax.naming.NamingEnumeration;
023: import javax.naming.NamingException;
024: import javax.naming.directory.Attribute;
025: import javax.naming.directory.Attributes;
026: import javax.naming.directory.DirContext;
027: import javax.naming.directory.SearchResult;
028: import java.util.logging.Level;
029: import java.util.logging.Logger;
030:
031: /**
032: * Represents an executor for LDAP search filter query(RFC 2254).
033: * <p>When {@link #execute(String)} is called a virtual
034: * row set based on {@link SearchResult search results} is produced.
035: * <p>The {@link javax.naming.directory.SearchResult#getAttributes()} produces
036: * columns for a virtual row. The virtual row also contains <code>dn</code> and <code>rdn</code>
037: * columns representing a found entry DN and a relative DN respectively.
038: *
039: * @author Fyodor Kupolov
040: * @version 1.0
041: */
042: public class SearchFilterQuery implements ParametersCallback {
043: private static final Logger LOG = Logger
044: .getLogger(SearchFilterQuery.class.getName());
045: private QueryCallback queryCallback;
046:
047: private SearchResult result;
048: private final LdapConnection connection;
049: private final PropertiesSubstitutor substitutor;
050:
051: /**
052: * Instantiates an LDAP query.
053: *
054: * @param connection ldap connection.
055: * @param parameters parent parameters callback to get unresolved variables from.
056: * @param queryCallback query callback to notify for search results.
057: */
058: public SearchFilterQuery(final LdapConnection connection,
059: final ParametersCallback parameters,
060: final QueryCallback queryCallback) {
061:
062: this .queryCallback = queryCallback;
063: this .connection = connection;
064: this .substitutor = new PropertiesSubstitutor(parameters);
065: }
066:
067: public Object getParameter(final String name) {
068: final Attributes attributes = result.getAttributes();
069: final Attribute attribute = attributes.get(name);
070: if (attribute != null) {
071: try {
072: return attribute.get(); //Currently only the first value is returned
073: } catch (NamingException e) {
074: throw new LdapProviderException(
075: "Failed to get attribute " + name + " value", e);
076: }
077: } else if ("dn".equalsIgnoreCase(name)) {
078: return result.getNameInNamespace(); //JDK14: use getName
079: }
080: return substitutor.getParameters().getParameter(name);
081: }
082:
083: /**
084: * Runs a search specified by filter on a {@link #connection}.
085: * <p>For each search result {@link QueryCallback#processRow(scriptella.spi.ParametersCallback)} is called.
086: *
087: * @param filter search filter according to RFC 2254
088: * @see DirContext#search(javax.naming.Name, String, javax.naming.directory.SearchControls)
089: */
090: public void execute(final String filter) {
091: //Using standard properties substitutor, may be change to something similar to JDBC parameters
092: final String sFilter = substitutor.substitute(filter);
093: if (LOG.isLoggable(Level.FINE)) {
094: LOG.fine("Running a query for search filter " + sFilter);
095: }
096: try {
097: iterate(query(connection, sFilter));
098: } catch (NamingException e) {
099: throw new LdapProviderException("Failed to execute query",
100: e);
101: } catch (LdapProviderException e2) {
102: //Settings a filter as a poblem statement if it has n't been set.
103: if (e2.getErrorStatement() != null) {
104: e2.setErrorStatement(sFilter);
105: }
106: throw e2;
107: }
108: }
109:
110: /**
111: * Iterates naming enumeration
112: */
113: private void iterate(NamingEnumeration<SearchResult> ne) {
114: try {
115: while (ne.hasMoreElements()) {
116: result = ne.nextElement();
117: if (LOG.isLoggable(Level.FINE)) {
118: LOG.fine("Processing search result: " + result);
119: }
120: queryCallback.processRow(this ); //notifying a callback
121: }
122: } finally {
123: try {//closing naming enumeration in case of unexpected error
124: ne.close();
125: } catch (Exception e) {
126: LOG.log(Level.FINE,
127: "Failed to close naming enumeration", e);
128: }
129: }
130: }
131:
132: protected NamingEnumeration<SearchResult> query(
133: final LdapConnection connection, final String filter)
134: throws NamingException {
135: NamingEnumeration<SearchResult> en = connection.getCtx()
136: .search(connection.getBaseDn(), filter,
137: connection.getSearchControls());
138: connection.getStatementCounter().statements++;
139: return en;
140: }
141:
142: }
|