diff --git a/activemq-core/src/main/java/org/apache/activemq/network/LdapNetworkConnector.java b/activemq-core/src/main/java/org/apache/activemq/network/LdapNetworkConnector.java new file mode 100644 index 0000000000..2d35438160 --- /dev/null +++ b/activemq-core/src/main/java/org/apache/activemq/network/LdapNetworkConnector.java @@ -0,0 +1,223 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.activemq.network; + +import java.net.URI; +import java.util.ArrayList; +import java.util.Hashtable; +import java.util.List; +import java.util.concurrent.CopyOnWriteArrayList; + +import javax.naming.Context; +import javax.naming.NamingEnumeration; +import javax.naming.directory.Attributes; +import javax.naming.directory.DirContext; +import javax.naming.directory.InitialDirContext; +import javax.naming.directory.SearchControls; +import javax.naming.directory.SearchResult; + +import org.apache.activemq.util.ServiceStopper; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +/** + * class to create dynamic network connectors listed in an directory + * server using the LDAP v3 protocol as defined in RFC 2251, the + * entries listed in the directory server must implement the ipHost + * and ipService objectClasses as defined in RFC 2307. + * + * @author Trevor Pounds + * @see RFC 2251 + * @see RFC 2307 + * + * @org.apache.xbean.XBean element="ldapNetworkConnector" + */ +public class LdapNetworkConnector extends NetworkConnector { + private static final Log LOG = LogFactory.getLog(LdapNetworkConnector.class); + + // TODO: future >> LDAP JNDI event handling to update connectors? + + // force returned entries to implement the ipHost and ipService objectClasses (RFC 2307) + private static final String REQUIRED_OBJECT_CLASS_FILTER = "(&(objectClass=ipHost)(objectClass=ipService))"; + + // required + private URI ldapURI; + private String ldapBase; + private String ldapUser; + private String ldapPassword; + + // optional + private int ldapSearchScope = SearchControls.OBJECT_SCOPE; + private String ldapSearchFilter = REQUIRED_OBJECT_CLASS_FILTER; + + // internal configurables + private DirContext ldapContext; + private List connectors = new CopyOnWriteArrayList(); + + /** + * default constructor + */ + public LdapNetworkConnector() { + } + + /** + * sets the LDAP server URI + * + * @param uri LDAP server URI + */ + public void setUri(URI uri) { + ldapURI = uri; + } + + /** + * sets the base LDAP dn used for lookup operations + * + * @param base LDAP base dn + */ + public void setBase(String base) { + ldapBase = base; + } + + /** + * sets the LDAP user for access credentials + * + * @param user LDAP dn of user + */ + public void setUser(String user) { + ldapUser = user; + } + + /** + * sets the LDAP password for access credentials + * + * @param password user password + */ + public void setPassword(String password) { + ldapPassword = password; + } + + /** + * sets the LDAP search scope + * + * @param searchScope LDAP JNDI search scope + */ + public void setSearchScope(String searchScope) throws Exception { + if(searchScope.equals("OBJECT_SCOPE")) { + ldapSearchScope = SearchControls.OBJECT_SCOPE; + } + else if(searchScope.equals("ONELEVEL_SCOPE")) { + ldapSearchScope = SearchControls.ONELEVEL_SCOPE; + } + else if(searchScope.equals("SUBTREE_SCOPE")) { + ldapSearchScope = SearchControls.SUBTREE_SCOPE; + } + else { + throw new Exception("ERR: unknown LDAP search scope specified: " + searchScope); + } + } + + /** + * sets the LDAP search filter as defined in RFC 2254 + * + * @param searchFilter LDAP search filter + * @see RFC 2254 + */ + public void setSearchFilter(String searchFilter) { + ldapSearchFilter = "(&" + REQUIRED_OBJECT_CLASS_FILTER + "(" + searchFilter + "))"; + } + + /** + * start the connector + */ + // XXX: this method seems awfully redundant when looking through the + // call stack when used in NetworkConnector based objects. I don't + // see why derived classes shouldn't just override the start/stop methods + protected void handleStart() throws Exception { + initLdapContext(); + for(URI uri : getLdapUris()) { + NetworkConnector connector = getBrokerService().addNetworkConnector(uri); + connector.start(); + connectors.add(connector); + } + super.handleStart(); + } + + /** + * stop the connector + * + * @param stopper service stopper object + */ + // XXX: this method seems awfully redundant when looking through the + // call stack when used in NetworkConnector based objects. I don't + // see why derived classes shouldn't just override the start/stop methods + protected void handleStop(ServiceStopper stopper) throws Exception { + for(NetworkConnector connector : connectors) { + getBrokerService().removeNetworkConnector(connector); + connector.stop(); + } + ldapContext.close(); + super.handleStop(stopper); + } + + /** + * returns the name of the connector + * + * @return connector name + */ + // XXX: this should probably be fixed elsewhere for all + // NetworkConnector derivatives...this impl does not + // seem to be well thought out? + public String getName() { + return toString(); + } + + /** + * initializes the LDAP JNDI context with the configured parameters + */ + protected void initLdapContext() throws Exception { + Hashtable env = new Hashtable(); + env.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.ldap.LdapCtxFactory"); + env.put(Context.PROVIDER_URL, ldapURI.toString()); + env.put(Context.SECURITY_PRINCIPAL, ldapUser); + env.put(Context.SECURITY_CREDENTIALS, ldapPassword); + ldapContext = new InitialDirContext(env); + } + + /** + * retrieves URIs matching the search filter via LDAP + * and creates network connectors based on the entries + * + * @returns list of retrieved URIs + */ + protected List getLdapUris() throws Exception { + SearchControls controls = new SearchControls(); + controls.setSearchScope(ldapSearchScope); + NamingEnumeration results = ldapContext.search(ldapBase, ldapSearchFilter, controls); + + List uriList = new ArrayList(); + while(results.hasMore()) { + Attributes attributes = results.next().getAttributes(); + String address = (String)attributes.get("iphostnumber").get(); + String port = (String)attributes.get("ipserviceport").get(); + String protocol = (String)attributes.get("ipserviceprotocol").get(); + URI uri = new URI("static:(" + protocol + "://" + address + ":" + port + ")"); + LOG.info("Discovered URI " + uri); + uriList.add(uri); + } + return uriList; + } +}