1 /* 2 * Copyright (c) 1999, 2006, Oracle and/or its affiliates. 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. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * 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 }