001: /* Copyright 2001, 2004 The JA-SIG Collaborative. All rights reserved.
002: * See license distributed with this file and
003: * available online at http://www.uportal.org/license.html
004: */
005:
006: package org.jasig.portal.ldap;
007:
008: import java.io.IOException;
009: import java.io.InputStream;
010: import java.util.Collections;
011: import java.util.Hashtable;
012: import java.util.Map;
013: import java.util.Properties;
014:
015: import javax.xml.xpath.XPath;
016: import javax.xml.xpath.XPathConstants;
017: import javax.xml.xpath.XPathExpressionException;
018: import javax.xml.xpath.XPathFactory;
019:
020: import org.apache.commons.logging.Log;
021: import org.apache.commons.logging.LogFactory;
022:
023: import org.jasig.portal.utils.ResourceLoader;
024: import org.w3c.dom.Document;
025: import org.w3c.dom.Element;
026: import org.w3c.dom.NamedNodeMap;
027: import org.w3c.dom.Node;
028: import org.w3c.dom.NodeList;
029: import org.w3c.dom.Text;
030:
031: /**
032: * Provides LDAP access in a way similar to a relational DBMS. This class
033: * was modified for the 2.4 release to function more like {@link org.jasig.portal.RDBMServices}.
034: * The class should be used via the static {@link #getDefaultLdapServer()} and
035: * {@link #getLdapServer(String name)} methods.
036: *
037: * @author Eric Dalquist <a href="mailto:edalquist@unicon.net">edalquist@unicon.net</a>
038: * @version $Revision: 35386 $
039: */
040: public final class LdapServices {
041:
042: private static final Log log = LogFactory
043: .getLog(LdapServices.class);
044:
045: /**
046: * The special name which can be used by clients to request the ILdapServer
047: * representing that configured in the .properties file rather (as opposed to
048: * the named servers configured in the XML configuration file.)
049: */
050: public static final String DEFAULT_LDAP_SERVER = "DEFAULT_LDAP_SERVER";
051:
052: private static final String PROPERTIES_PATH = "/properties/";
053: private static final String LDAP_PROPERTIES_FILE = PROPERTIES_PATH
054: + "ldap.properties";
055: private static final String LDAP_XML_FILE = PROPERTIES_PATH
056: + "ldap.xml";
057: private static final String LDAP_XML_CONNECTION_XPATH = "ldapConnections/connection";
058:
059: /**
060: * Map from server names to ILdapServer objects.
061: */
062: private static final Map ldapConnections = new Hashtable();
063: private static ILdapServer defaultConn = null;
064:
065: /**
066: * Load the ILdapServers in response to our configuration.
067: */
068: static {
069: loadLdapProperties();
070:
071: loadLdapXml();
072:
073: //Make sure a default connection was created.
074: if (defaultConn == null) {
075: RuntimeException re = new IllegalStateException(
076: "No default connection was created during initialization.");
077: log.error(re.getMessage(), re);
078: throw re;
079: }
080: }
081:
082: /**
083: * Load LDAP servers from the XML configuration file.
084: */
085: private static void loadLdapXml() {
086: //Read extra connections from ldap.xml
087: Document config = null;
088: try {
089: config = ResourceLoader.getResourceAsDocument(
090: LdapServices.class, LDAP_XML_FILE);
091: } catch (Exception e) {
092: log.error(
093: "Could not create Document from " + LDAP_XML_FILE,
094: e);
095: }
096:
097: if (config != null) {
098: config.normalize();
099:
100: try {
101: XPathFactory fac = XPathFactory.newInstance();
102: XPath xpath = fac.newXPath();
103: NodeList connElements = (NodeList) xpath.evaluate(
104: LDAP_XML_CONNECTION_XPATH, config,
105: XPathConstants.NODESET);
106:
107: //Loop through each <connection> element
108: for (int connIndex = 0; connIndex < connElements
109: .getLength(); connIndex++) {
110: Node connElement = connElements.item(connIndex);
111:
112: try {
113: if (connElement instanceof Element) {
114: //See if this connection is flagged as default
115: NamedNodeMap connAtts = connElement
116: .getAttributes();
117: Node defaultFlagAtt = connAtts
118: .getNamedItem("default");
119:
120: boolean isDefaultConn;
121: if (defaultFlagAtt != null)
122: isDefaultConn = (new Boolean(
123: defaultFlagAtt.getNodeValue()))
124: .booleanValue();
125: else
126: isDefaultConn = false;
127:
128: String name = null;
129: String host = null;
130: String port = null;
131: String baseDN = null;
132: String managerDN = null;
133: String managerPW = null;
134: String uidAttribute = null;
135: boolean useSsl = false;
136: String factory = null;
137:
138: //Loop through all the child nodes of the connection
139: NodeList connParams = connElement
140: .getChildNodes();
141: for (int connParamIndex = 0; connParamIndex < connParams
142: .getLength(); connParamIndex++) {
143: Node connParam = connParams
144: .item(connParamIndex);
145:
146: if (connParam instanceof Element) {
147: String tagName = ((Element) connParam)
148: .getTagName();
149: String tagValue = null;
150:
151: if (connParam.getFirstChild() instanceof Text) {
152: tagValue = ((Text) connParam
153: .getFirstChild())
154: .getData();
155: }
156:
157: if (tagName.equals("name")) {
158: name = tagValue;
159: } else if (tagName.equals("host")) {
160: host = tagValue;
161: } else if (tagName.equals("port")) {
162: port = tagValue;
163: } else if (tagName.equals("baseDN")) {
164: baseDN = tagValue;
165: } else if (tagName
166: .equals("managerDN")) {
167: managerDN = tagValue;
168: } else if (tagName
169: .equals("managerPW")) {
170: managerPW = tagValue;
171: } else if (tagName
172: .equals("uidAttribute")) {
173: uidAttribute = tagValue;
174: } else if (tagName
175: .equals("protocol")) {
176: useSsl = "ssl".equals(tagValue);
177: } else if (tagName
178: .equals("factory")) {
179: factory = tagValue;
180: }
181: }
182: }
183:
184: //Create a new ILdapServer
185: if (name != null) {
186: try {
187: ILdapServer newConn = new LdapServerImpl(
188: name, host, port, baseDN,
189: uidAttribute, managerDN,
190: managerPW, useSsl, factory);
191: ldapConnections.put(name, newConn);
192:
193: if (isDefaultConn) {
194: defaultConn = newConn;
195: if (log.isInfoEnabled())
196: log
197: .info("Replaced '"
198: + LDAP_PROPERTIES_FILE
199: + "' connection with default connection '"
200: + name
201: + "' from '"
202: + LDAP_XML_FILE
203: + "'");
204: }
205: } catch (IllegalArgumentException iae) {
206: if (log.isInfoEnabled())
207: log
208: .info(
209: "Invalid data for server "
210: + name
211: + " in "
212: + LDAP_XML_FILE,
213: iae);
214: }
215: } else {
216: log
217: .error("Error creating ILdapServer, no name specified.");
218: }
219: }
220: } catch (Exception e) {
221: log.error(
222: "Error creating ILdapServer from node: "
223: + connElement.getNodeName(), e);
224: }
225: }
226: } catch (XPathExpressionException e) {
227: log.error("Error applying XPath query ("
228: + LDAP_XML_CONNECTION_XPATH + ") on "
229: + LDAP_XML_FILE, e);
230: }
231: } else {
232: log.error("No document was loaded from " + LDAP_XML_FILE);
233: }
234: }
235:
236: /**
237: * Load LDAP server from ldap.properties.
238: */
239: private static void loadLdapProperties() {
240:
241: //This try/catch/finaly block reads the default LDAP connection
242: //from a properties file.
243: InputStream ins = null;
244: try {
245: //Read properties file
246: ins = LdapServices.class
247: .getResourceAsStream(LDAP_PROPERTIES_FILE);
248:
249: //If the properties file was found
250: if (ins != null) {
251: Properties ldapProps = new Properties();
252: ldapProps.load(ins);
253:
254: try {
255: //Create the default connection object
256: defaultConn = new LdapServerImpl(
257: "ldap.properties configured connection",
258: ldapProps.getProperty("ldap.host"),
259: ldapProps.getProperty("ldap.port"),
260: ldapProps.getProperty("ldap.baseDN"),
261: ldapProps.getProperty("ldap.uidAttribute"),
262: ldapProps.getProperty("ldap.managerDN"),
263: ldapProps.getProperty("ldap.managerPW"),
264: "ssl".equals(ldapProps
265: .getProperty("ldap.protocol")),
266: ldapProps.getProperty("ldap.factory"));
267:
268: } catch (IllegalArgumentException iae) {
269: if (log.isInfoEnabled())
270: log.info("Invalid data in "
271: + LDAP_PROPERTIES_FILE, iae);
272: }
273: } else {
274: if (log.isInfoEnabled())
275: log.info(LDAP_PROPERTIES_FILE
276: + " was not found, all ldap "
277: + "connections will be loaded from "
278: + LDAP_XML_FILE);
279: }
280: } catch (Exception e) {
281: log.error(
282: "LdapServices::initConnections(): Error while loading "
283: + "default ldap connection from "
284: + LDAP_PROPERTIES_FILE, e);
285: } finally {
286: try {
287: if (ins != null)
288: ins.close();
289: } catch (IOException ioe) {
290: log.error("Unable to close " + LDAP_PROPERTIES_FILE
291: + " InputStream ", ioe);
292: }
293: }
294:
295: }
296:
297: /**
298: * Get the default {@link ILdapServer}.
299: * @return The default {@link ILdapServer}.
300: */
301: public static ILdapServer getDefaultLdapServer() {
302: return defaultConn;
303: }
304:
305: /**
306: * Get a named {@link ILdapServer}.
307: * Using the special name 'DEFAULT_LDAP_SERVER' causes this method to
308: * return the default Ldap server.
309: * @param name The name of the ILdapServer to return.
310: * @return An {@link ILdapServer} with the specified name,
311: * <code>null</code> if there is no connection with the specified name.
312: */
313: public static ILdapServer getLdapServer(String name) {
314: /**
315: * If the request is for the special name indicating the default Ldap server
316: * return that rather than looking in the map of named servers.
317: */
318: if (LdapServices.DEFAULT_LDAP_SERVER.equals(name))
319: return getDefaultLdapServer();
320:
321: return (ILdapServer) ldapConnections.get(name);
322: }
323:
324: /**
325: * Get a {@link Map} of {@link ILdapServer} instances.
326: * If a server is configured in ldap.properties it will not be available
327: * in the {@link Map}. They key is the server name, value is the
328: * {@link ILdapServer} instance
329: *
330: * @return A {@link Map} of {@link ILdapServer} instances.
331: */
332: public static Map getLdapServerMap() {
333: return Collections.unmodifiableMap(ldapConnections);
334: }
335:
336: /**
337: * This class only provides static methods.
338: */
339: private LdapServices() {
340: // private constructor prevents instantiation of this
341: // static service-providing class
342: }
343: }
|