1 /*
2 * Copyright 1999-2006 Sun Microsystems, Inc. All Rights Reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation. Sun designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Sun in the LICENSE file that accompanied this code.
10 *
11 * This code is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14 * version 2 for more details (a copy is included in the LICENSE file that
15 * accompanied this code).
16 *
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
22 * CA 95054 USA or visit www.sun.com if you need additional information or
23 * have any questions.
24 */
25
26 package com.sun.jndi.ldap;
27
28 import java.util.Hashtable;
29 import java.util.Vector;
30 import java.util.Enumeration;
31 import java.net.MalformedURLException;
32
33 import javax.naming;
34 import javax.naming.directory;
35 import javax.naming.spi.ObjectFactory;
36 import javax.naming.spi.InitialContextFactory;
37 import javax.naming.ldap.Control;
38
39 import com.sun.jndi.url.ldap.ldapURLContextFactory;
40
41 final public class LdapCtxFactory implements ObjectFactory, InitialContextFactory {
42 /**
43 * The type of each address in an LDAP reference.
44 */
45 public final static String ADDRESS_TYPE = "URL";
46
47 // ----------------- ObjectFactory interface --------------------
48
49 public Object getObjectInstance(Object ref, Name name, Context nameCtx,
50 Hashtable<?,?> env) throws Exception {
51
52 if (!isLdapRef(ref)) {
53 return null;
54 }
55 ObjectFactory factory = new ldapURLContextFactory();
56 String[] urls = getURLs((Reference)ref);
57 return factory.getObjectInstance(urls, name, nameCtx, env);
58 }
59
60 // ----------------- InitialContext interface --------------------
61
62 public Context getInitialContext(Hashtable<?,?> envprops)
63 throws NamingException {
64
65 try {
66 String providerUrl = (envprops != null) ?
67 (String)envprops.get(Context.PROVIDER_URL) : null;
68
69 // If URL not in environment, use defaults
70 if (providerUrl == null) {
71 return new LdapCtx("", LdapCtx.DEFAULT_HOST,
72 LdapCtx.DEFAULT_PORT, envprops, false);
73 }
74
75 // Extract URL(s)
76 String[] urls = LdapURL.fromList(providerUrl);
77
78 if (urls.length == 0) {
79 throw new ConfigurationException(Context.PROVIDER_URL +
80 " property does not contain a URL");
81 }
82
83 // Generate an LDAP context
84 return getLdapCtxInstance(urls, envprops);
85
86 } catch (LdapReferralException e) {
87
88 if (envprops != null &&
89 "throw".equals(envprops.get(Context.REFERRAL))) {
90 throw e;
91 }
92
93 Control[] bindCtls = (envprops != null)?
94 (Control[])envprops.get(LdapCtx.BIND_CONTROLS) : null;
95
96 return (LdapCtx)e.getReferralContext(envprops, bindCtls);
97 }
98 }
99
100 /**
101 * Returns true if argument is an LDAP reference.
102 */
103 private static boolean isLdapRef(Object obj) {
104
105 if (!(obj instanceof Reference)) {
106 return false;
107 }
108 String thisClassName = LdapCtxFactory.class.getName();
109 Reference ref = (Reference)obj;
110
111 return thisClassName.equals(ref.getFactoryClassName());
112 }
113
114 /**
115 * Returns the URLs contained within an LDAP reference.
116 */
117 private static String[] getURLs(Reference ref) throws NamingException {
118
119 int size = 0; // number of URLs
120 String[] urls = new String[ref.size()];
121
122 Enumeration addrs = ref.getAll();
123 while (addrs.hasMoreElements()) {
124 RefAddr addr = (RefAddr)addrs.nextElement();
125
126 if ((addr instanceof StringRefAddr) &&
127 addr.getType().equals(ADDRESS_TYPE)) {
128
129 urls[size++] = (String)addr.getContent();
130 }
131 }
132 if (size == 0) {
133 throw (new ConfigurationException(
134 "Reference contains no valid addresses"));
135 }
136
137 // Trim URL array down to size.
138 if (size == ref.size()) {
139 return urls;
140 }
141 String[] urls2 = new String[size];
142 System.arraycopy(urls, 0, urls2, 0, size);
143 return urls2;
144 }
145
146 // ------------ Utilities used by other classes ----------------
147
148 public static DirContext getLdapCtxInstance(Object urlInfo, Hashtable env)
149 throws NamingException {
150
151 if (urlInfo instanceof String) {
152 return getUsingURL((String)urlInfo, env);
153 } else if (urlInfo instanceof String[]) {
154 return getUsingURLs((String[])urlInfo, env);
155 } else {
156 throw new IllegalArgumentException(
157 "argument must be an LDAP URL String or array of them");
158 }
159 }
160
161 private static DirContext getUsingURL(String url, Hashtable env)
162 throws NamingException {
163 DirContext ctx = null;
164 LdapURL ldapUrl = new LdapURL(url);
165 String dn = ldapUrl.getDN();
166 String host = ldapUrl.getHost();
167 int port = ldapUrl.getPort();
168 String[] hostports;
169 String domainName = null;
170
171 // handle a URL with no hostport (ldap:/// or ldaps:///)
172 // locate the LDAP service using the URL's distinguished name
173 if (host == null &&
174 port == -1 &&
175 dn != null &&
176 (domainName = ServiceLocator.mapDnToDomainName(dn)) != null &&
177 (hostports = ServiceLocator.getLdapService(domainName, env))
178 != null) {
179 // Generate new URLs that include the discovered hostports.
180 // Reuse the original URL scheme.
181 String scheme = ldapUrl.getScheme() + "://";
182 String[] newUrls = new String[hostports.length];
183 String query = ldapUrl.getQuery();
184 String urlSuffix = ldapUrl.getPath() + (query != null ? query : "");
185 for (int i = 0; i < hostports.length; i++) {
186 newUrls[i] = scheme + hostports[i] + urlSuffix;
187 }
188 ctx = getUsingURLs(newUrls, env);
189 // Associate the derived domain name with the context
190 ((LdapCtx)ctx).setDomainName(domainName);
191
192 } else {
193 ctx = new LdapCtx(dn, host, port, env, ldapUrl.useSsl());
194 // Record the URL that created the context
195 ((LdapCtx)ctx).setProviderUrl(url);
196 }
197 return ctx;
198 }
199
200 /*
201 * Try each URL until one of them succeeds.
202 * If all URLs fail, throw one of the exceptions arbitrarily.
203 * Not pretty, but potentially more informative than returning null.
204 */
205 private static DirContext getUsingURLs(String[] urls, Hashtable env)
206 throws NamingException {
207 NamingException ne = null;
208 DirContext ctx = null;
209 for (int i = 0; i < urls.length; i++) {
210 try {
211 return getUsingURL(urls[i], env);
212 } catch (AuthenticationException e) {
213 throw e;
214 } catch (NamingException e) {
215 ne = e;
216 }
217 }
218 throw ne;
219 }
220
221 /**
222 * Used by Obj and obj/RemoteToAttrs too so must be public
223 */
224 public static Attribute createTypeNameAttr(Class cl) {
225 Vector v = new Vector(10);
226 String[] types = getTypeNames(cl, v);
227 if (types.length > 0) {
228 BasicAttribute tAttr =
229 new BasicAttribute(Obj.JAVA_ATTRIBUTES[Obj.TYPENAME]);
230 for (int i = 0; i < types.length; i++) {
231 tAttr.add(types[i]);
232 }
233 return tAttr;
234 }
235 return null;
236 }
237
238 private static String[] getTypeNames(Class currentClass, Vector v) {
239
240 getClassesAux(currentClass, v);
241 Class[] members = currentClass.getInterfaces();
242 for (int i = 0; i < members.length; i++) {
243 getClassesAux(members[i], v);
244 }
245 String[] ret = new String[v.size()];
246 int i = 0;
247 for (java.util.Enumeration e = v.elements(); e.hasMoreElements();) {
248 ret[i++] = (String)e.nextElement();
249 }
250 return ret;
251 }
252
253 private static void getClassesAux(Class currentClass, Vector v) {
254 if (!v.contains(currentClass.getName())) {
255 v.addElement(currentClass.getName());
256 }
257 currentClass = currentClass.getSuperclass();
258
259 while (currentClass != null) {
260 getTypeNames(currentClass, v);
261 currentClass = currentClass.getSuperclass();
262 }
263 }
264 }