1 /*
   2  * Copyright (c) 1999, 2005, 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 javax.naming.*;
  29 import javax.naming.directory.*;
  30 import javax.naming.spi.DirectoryManager;
  31 import javax.naming.spi.DirStateFactory;
  32 
  33 import java.io.IOException;
  34 import java.io.ByteArrayInputStream;
  35 import java.io.ByteArrayOutputStream;
  36 import java.io.ObjectInputStream;
  37 import java.io.ObjectOutputStream;
  38 import java.io.ObjectStreamClass;
  39 import java.io.InputStream;
  40 
  41 import java.util.Hashtable;
  42 import java.util.Vector;
  43 import java.util.StringTokenizer;
  44 
  45 import sun.misc.BASE64Encoder;
  46 import sun.misc.BASE64Decoder;
  47 
  48 import java.lang.reflect.Proxy;
  49 import java.lang.reflect.Modifier;
  50 
  51 /**
  52   * Class containing static methods and constants for dealing with
  53   * encoding/decoding JNDI References and Serialized Objects
  54   * in LDAP.
  55   * @author Vincent Ryan
  56   * @author Rosanna Lee
  57   */
  58 final class Obj {
  59 
  60     private Obj () {}; // Make sure no one can create one
  61 
  62     // package private; used by Connection
  63     static VersionHelper helper = VersionHelper.getVersionHelper();
  64 
  65     // LDAP attributes used to support Java objects.
  66     static final String[] JAVA_ATTRIBUTES = {
  67         "objectClass",
  68         "javaSerializedData",
  69         "javaClassName",
  70         "javaFactory",
  71         "javaCodeBase",
  72         "javaReferenceAddress",
  73         "javaClassNames",
  74         "javaRemoteLocation"     // Deprecated
  75     };
  76 
  77     static final int OBJECT_CLASS = 0;
  78     static final int SERIALIZED_DATA = 1;
  79     static final int CLASSNAME = 2;
  80     static final int FACTORY = 3;
  81     static final int CODEBASE = 4;
  82     static final int REF_ADDR = 5;
  83     static final int TYPENAME = 6;
  84     /**
  85      * @deprecated
  86      */
  87     private static final int REMOTE_LOC = 7;
  88 
  89     // LDAP object classes to support Java objects
  90     static final String[] JAVA_OBJECT_CLASSES = {
  91         "javaContainer",
  92         "javaObject",
  93         "javaNamingReference",
  94         "javaSerializedObject",
  95         "javaMarshalledObject",
  96     };
  97 
  98     static final String[] JAVA_OBJECT_CLASSES_LOWER = {
  99         "javacontainer",
 100         "javaobject",
 101         "javanamingreference",
 102         "javaserializedobject",
 103         "javamarshalledobject",
 104     };
 105 
 106     static final int STRUCTURAL = 0;    // structural object class
 107     static final int BASE_OBJECT = 1;   // auxiliary java object class
 108     static final int REF_OBJECT = 2;    // auxiliary reference object class
 109     static final int SER_OBJECT = 3;    // auxiliary serialized object class
 110     static final int MAR_OBJECT = 4;    // auxiliary marshalled object class
 111 
 112     /**
 113      * Encode an object in LDAP attributes.
 114      * Supports binding Referenceable or Reference, Serializable,
 115      * and DirContext.
 116      *
 117      * If the object supports the Referenceable interface then encode
 118      * the reference to the object. See encodeReference() for details.
 119      *<p>
 120      * If the object is serializable, it is stored as follows:
 121      * javaClassName
 122      *   value: Object.getClass();
 123      * javaSerializedData
 124      *   value: serialized form of Object (in binary form).
 125      * javaTypeName
 126      *   value: getTypeNames(Object.getClass());
 127      */
 128     private static Attributes encodeObject(char separator,
 129         Object obj, Attributes attrs,
 130         Attribute objectClass, boolean cloned)
 131         throws NamingException {
 132             boolean structural =
 133                 (objectClass.size() == 0 ||
 134                     (objectClass.size() == 1 && objectClass.contains("top")));
 135 
 136             if (structural) {
 137                 objectClass.add(JAVA_OBJECT_CLASSES[STRUCTURAL]);
 138             }
 139 
 140     // References
 141             if (obj instanceof Referenceable) {
 142                 objectClass.add(JAVA_OBJECT_CLASSES[BASE_OBJECT]);
 143                 objectClass.add(JAVA_OBJECT_CLASSES[REF_OBJECT]);
 144                 if (!cloned) {
 145                     attrs = (Attributes)attrs.clone();
 146                 }
 147                 attrs.put(objectClass);
 148                 return (encodeReference(separator,
 149                     ((Referenceable)obj).getReference(),
 150                     attrs, obj));
 151 
 152             } else if (obj instanceof Reference) {
 153                 objectClass.add(JAVA_OBJECT_CLASSES[BASE_OBJECT]);
 154                 objectClass.add(JAVA_OBJECT_CLASSES[REF_OBJECT]);
 155                 if (!cloned) {
 156                     attrs = (Attributes)attrs.clone();
 157                 }
 158                 attrs.put(objectClass);
 159                 return (encodeReference(separator, (Reference)obj, attrs, null));
 160 
 161     // Serializable Object
 162             } else if (obj instanceof java.io.Serializable) {
 163                 objectClass.add(JAVA_OBJECT_CLASSES[BASE_OBJECT]);
 164                 if (!(objectClass.contains(JAVA_OBJECT_CLASSES[MAR_OBJECT]) ||
 165                     objectClass.contains(JAVA_OBJECT_CLASSES_LOWER[MAR_OBJECT]))) {
 166                     objectClass.add(JAVA_OBJECT_CLASSES[SER_OBJECT]);
 167                 }
 168                 if (!cloned) {
 169                     attrs = (Attributes)attrs.clone();
 170                 }
 171                 attrs.put(objectClass);
 172                 attrs.put(new BasicAttribute(JAVA_ATTRIBUTES[SERIALIZED_DATA],
 173                     serializeObject(obj)));
 174                 if (attrs.get(JAVA_ATTRIBUTES[CLASSNAME]) == null) {
 175                     attrs.put(JAVA_ATTRIBUTES[CLASSNAME],
 176                         obj.getClass().getName());
 177                 }
 178                 if (attrs.get(JAVA_ATTRIBUTES[TYPENAME]) == null) {
 179                     Attribute tAttr =
 180                         LdapCtxFactory.createTypeNameAttr(obj.getClass());
 181                     if (tAttr != null) {
 182                         attrs.put(tAttr);
 183                     }
 184                 }
 185     // DirContext Object
 186             } else if (obj instanceof DirContext) {
 187                 // do nothing
 188             } else {
 189                 throw new IllegalArgumentException(
 190             "can only bind Referenceable, Serializable, DirContext");
 191             }
 192             //      System.err.println(attrs);
 193             return attrs;
 194     }
 195 
 196     /**
 197      * Each value in javaCodebase contains a list of space-separated
 198      * URLs. Each value is independent; we can pick any of the values
 199      * so we just use the first one.
 200      * @return an array of URL strings for the codebase
 201      */
 202     private static String[] getCodebases(Attribute codebaseAttr) throws
 203         NamingException {
 204         if (codebaseAttr == null) {
 205             return null;
 206         } else {
 207             StringTokenizer parser =
 208                 new StringTokenizer((String)codebaseAttr.get());
 209             Vector vec = new Vector(10);
 210             while (parser.hasMoreTokens()) {
 211                 vec.addElement(parser.nextToken());
 212             }
 213             String[] answer = new String[vec.size()];
 214             for (int i = 0; i < answer.length; i++) {
 215                 answer[i] = (String)vec.elementAt(i);
 216             }
 217             return answer;
 218         }
 219     }
 220 
 221     /*
 222      * Decode an object from LDAP attribute(s).
 223      * The object may be a Reference, or a Serialized object.
 224      *
 225      * See encodeObject() and encodeReference() for details on formats
 226      * expected.
 227      */
 228     static Object decodeObject(Attributes attrs)
 229         throws NamingException {
 230 
 231         Attribute attr;
 232 
 233         // Get codebase, which is used in all 3 cases.
 234         String[] codebases = getCodebases(attrs.get(JAVA_ATTRIBUTES[CODEBASE]));
 235         try {
 236             if ((attr = attrs.get(JAVA_ATTRIBUTES[SERIALIZED_DATA])) != null) {
 237                 ClassLoader cl = helper.getURLClassLoader(codebases);
 238                 return deserializeObject((byte[])attr.get(), cl);
 239             } else if ((attr = attrs.get(JAVA_ATTRIBUTES[REMOTE_LOC])) != null) {
 240                 // For backward compatibility only
 241                 return decodeRmiObject(
 242                     (String)attrs.get(JAVA_ATTRIBUTES[CLASSNAME]).get(),
 243                     (String)attr.get(), codebases);
 244             }
 245 
 246             attr = attrs.get(JAVA_ATTRIBUTES[OBJECT_CLASS]);
 247             if (attr != null &&
 248                 (attr.contains(JAVA_OBJECT_CLASSES[REF_OBJECT]) ||
 249                     attr.contains(JAVA_OBJECT_CLASSES_LOWER[REF_OBJECT]))) {
 250                 return decodeReference(attrs, codebases);
 251             }
 252             return null;
 253         } catch (IOException e) {
 254             NamingException ne = new NamingException();
 255             ne.setRootCause(e);
 256             throw ne;
 257         }
 258     }
 259 
 260     /**
 261      * Convert a Reference object into several LDAP attributes.
 262      *
 263      * A Reference is stored as into the following attributes:
 264      * javaClassName
 265      *   value: Reference.getClassName();
 266      * javaFactory
 267      *   value: Reference.getFactoryClassName();
 268      * javaCodeBase
 269      *   value: Reference.getFactoryClassLocation();
 270      * javaReferenceAddress
 271      *   value: #0#typeA#valA
 272      *   value: #1#typeB#valB
 273      *   value: #2#typeC##[serialized RefAddr C]
 274      *   value: #3#typeD#valD
 275      *
 276      * where
 277      * -  the first character denotes the separator
 278      * -  the number following the first separator denotes the position
 279      *    of the RefAddr within the Reference
 280      * -  "typeA" is RefAddr.getType()
 281      * -  ## denotes that the Base64-encoded form of the non-StringRefAddr
 282      *    is to follow; otherwise the value that follows is
 283      *    StringRefAddr.getContents()
 284      *
 285      * The default separator is the hash character (#).
 286      * May provide property for this in future.
 287      */
 288 
 289     private static Attributes encodeReference(char separator,
 290         Reference ref, Attributes attrs, Object orig)
 291         throws NamingException {
 292 
 293         if (ref == null)
 294             return attrs;
 295 
 296         String s;
 297 
 298         if ((s = ref.getClassName()) != null) {
 299             attrs.put(new BasicAttribute(JAVA_ATTRIBUTES[CLASSNAME], s));
 300         }
 301 
 302         if ((s = ref.getFactoryClassName()) != null) {
 303             attrs.put(new BasicAttribute(JAVA_ATTRIBUTES[FACTORY], s));
 304         }
 305 
 306         if ((s = ref.getFactoryClassLocation()) != null) {
 307             attrs.put(new BasicAttribute(JAVA_ATTRIBUTES[CODEBASE], s));
 308         }
 309 
 310         // Get original object's types if caller has not explicitly
 311         // specified other type names
 312         if (orig != null && attrs.get(JAVA_ATTRIBUTES[TYPENAME]) != null) {
 313             Attribute tAttr =
 314                 LdapCtxFactory.createTypeNameAttr(orig.getClass());
 315             if (tAttr != null) {
 316                 attrs.put(tAttr);
 317             }
 318         }
 319 
 320         int count = ref.size();
 321 
 322         if (count > 0) {
 323 
 324             Attribute refAttr = new BasicAttribute(JAVA_ATTRIBUTES[REF_ADDR]);
 325             RefAddr refAddr;
 326             BASE64Encoder encoder = null;
 327 
 328             for (int i = 0; i < count; i++) {
 329                 refAddr = ref.get(i);
 330 
 331                 if (refAddr instanceof StringRefAddr) {
 332                     refAttr.add(""+ separator + i +
 333                         separator +     refAddr.getType() +
 334                         separator + refAddr.getContent());
 335                 } else {
 336                     if (encoder == null)
 337                         encoder = new BASE64Encoder();
 338 
 339                     refAttr.add(""+ separator + i +
 340                         separator + refAddr.getType() +
 341                         separator + separator +
 342                         encoder.encodeBuffer(serializeObject(refAddr)));
 343                 }
 344             }
 345             attrs.put(refAttr);
 346         }
 347         return attrs;
 348     }
 349 
 350     /*
 351      * A RMI object is stored in the directory as
 352      * javaClassName
 353      *   value: Object.getClass();
 354      * javaRemoteLocation
 355      *   value: URL of RMI object (accessed through the RMI Registry)
 356      * javaCodebase:
 357      *   value: URL of codebase of where to find classes for object
 358      *
 359      * Return the RMI Location URL itself. This will be turned into
 360      * an RMI object when getObjectInstance() is called on it.
 361      * %%% Ignore codebase for now. Depend on RMI registry to send code.-RL
 362      * @deprecated For backward compatibility only
 363      */
 364     private static Object decodeRmiObject(String className,
 365         String rmiName, String[] codebases) throws NamingException {
 366             return new Reference(className, new StringRefAddr("URL", rmiName));
 367     }
 368 
 369     /*
 370      * Restore a Reference object from several LDAP attributes
 371      */
 372     private static Reference decodeReference(Attributes attrs,
 373         String[] codebases) throws NamingException, IOException {
 374 
 375         Attribute attr;
 376         String className;
 377         String factory = null;
 378 
 379         if ((attr = attrs.get(JAVA_ATTRIBUTES[CLASSNAME])) != null) {
 380             className = (String)attr.get();
 381         } else {
 382             throw new InvalidAttributesException(JAVA_ATTRIBUTES[CLASSNAME] +
 383                         " attribute is required");
 384         }
 385 
 386         if ((attr = attrs.get(JAVA_ATTRIBUTES[FACTORY])) != null) {
 387             factory = (String)attr.get();
 388         }
 389 
 390         Reference ref = new Reference(className, factory,
 391             (codebases != null? codebases[0] : null));
 392 
 393         /*
 394          * string encoding of a RefAddr is either:
 395          *
 396          *      #posn#<type>#<address>
 397          * or
 398          *      #posn#<type>##<base64-encoded address>
 399          */
 400         if ((attr = attrs.get(JAVA_ATTRIBUTES[REF_ADDR])) != null) {
 401 
 402             String val, posnStr, type;
 403             char separator;
 404             int start, sep, posn;
 405             BASE64Decoder decoder = null;
 406 
 407             ClassLoader cl = helper.getURLClassLoader(codebases);
 408 
 409             /*
 410              * Temporary Vector for decoded RefAddr addresses - used to ensure
 411              * unordered addresses are correctly re-ordered.
 412              */
 413             Vector refAddrList = new Vector();
 414             refAddrList.setSize(attr.size());
 415 
 416             for (NamingEnumeration vals = attr.getAll(); vals.hasMore(); ) {
 417 
 418                 val = (String)vals.next();
 419 
 420                 if (val.length() == 0) {
 421                     throw new InvalidAttributeValueException(
 422                         "malformed " + JAVA_ATTRIBUTES[REF_ADDR] + " attribute - "+
 423                         "empty attribute value");
 424                 }
 425                 // first character denotes encoding separator
 426                 separator = val.charAt(0);
 427                 start = 1;  // skip over separator
 428 
 429                 // extract position within Reference
 430                 if ((sep = val.indexOf(separator, start)) < 0) {
 431                     throw new InvalidAttributeValueException(
 432                         "malformed " + JAVA_ATTRIBUTES[REF_ADDR] + " attribute - " +
 433                         "separator '" + separator + "'" + "not found");
 434                 }
 435                 if ((posnStr = val.substring(start, sep)) == null) {
 436                     throw new InvalidAttributeValueException(
 437                         "malformed " + JAVA_ATTRIBUTES[REF_ADDR] + " attribute - " +
 438                         "empty RefAddr position");
 439                 }
 440                 try {
 441                     posn = Integer.parseInt(posnStr);
 442                 } catch (NumberFormatException nfe) {
 443                     throw new InvalidAttributeValueException(
 444                         "malformed " + JAVA_ATTRIBUTES[REF_ADDR] + " attribute - " +
 445                         "RefAddr position not an integer");
 446                 }
 447                 start = sep + 1; // skip over position and trailing separator
 448 
 449                 // extract type
 450                 if ((sep = val.indexOf(separator, start)) < 0) {
 451                     throw new InvalidAttributeValueException(
 452                         "malformed " + JAVA_ATTRIBUTES[REF_ADDR] + " attribute - " +
 453                         "RefAddr type not found");
 454                 }
 455                 if ((type = val.substring(start, sep)) == null) {
 456                     throw new InvalidAttributeValueException(
 457                         "malformed " + JAVA_ATTRIBUTES[REF_ADDR] + " attribute - " +
 458                         "empty RefAddr type");
 459                 }
 460                 start = sep + 1; // skip over type and trailing separator
 461 
 462                 // extract content
 463                 if (start == val.length()) {
 464                     // Empty content
 465                     refAddrList.setElementAt(new StringRefAddr(type, null), posn);
 466                 } else if (val.charAt(start) == separator) {
 467                     // Double separators indicate a non-StringRefAddr
 468                     // Content is a Base64-encoded serialized RefAddr
 469 
 470                     ++start;  // skip over consecutive separator
 471                     // %%% RL: exception if empty after double separator
 472 
 473                     if (decoder == null)
 474                         decoder = new BASE64Decoder();
 475 
 476                     RefAddr ra = (RefAddr)
 477                         deserializeObject(
 478                             decoder.decodeBuffer(val.substring(start)),
 479                             cl);
 480 
 481                     refAddrList.setElementAt(ra, posn);
 482                 } else {
 483                     // Single separator indicates a StringRefAddr
 484                     refAddrList.setElementAt(new StringRefAddr(type,
 485                         val.substring(start)), posn);
 486                 }
 487             }
 488 
 489             // Copy to real reference
 490             for (int i = 0; i < refAddrList.size(); i++) {
 491                 ref.add((RefAddr)refAddrList.elementAt(i));
 492             }
 493         }
 494 
 495         return (ref);
 496     }
 497 
 498     /*
 499      * Serialize an object into a byte array
 500      */
 501     private static byte[] serializeObject(Object obj) throws NamingException {
 502 
 503         try {
 504             ByteArrayOutputStream bytes = new ByteArrayOutputStream();
 505             ObjectOutputStream serial = new ObjectOutputStream(bytes);
 506             serial.writeObject(obj);
 507             serial.close();
 508 
 509             return (bytes.toByteArray());
 510 
 511         } catch (IOException e) {
 512             NamingException ne = new NamingException();
 513             ne.setRootCause(e);
 514             throw ne;
 515         }
 516     }
 517 
 518     /*
 519      * Deserializes a byte array into an object.
 520      */
 521     private static Object deserializeObject(byte[] obj, ClassLoader cl)
 522         throws NamingException {
 523 
 524         try {
 525             // Create ObjectInputStream for deserialization
 526             ByteArrayInputStream bytes = new ByteArrayInputStream(obj);
 527             ObjectInputStream deserial = (cl == null ?
 528                 new ObjectInputStream(bytes) :
 529                 new LoaderInputStream(bytes, cl));
 530 
 531             try {
 532                 return deserial.readObject();
 533             } catch (ClassNotFoundException e) {
 534                 NamingException ne = new NamingException();
 535                 ne.setRootCause(e);
 536                 throw ne;
 537             } finally {
 538                 deserial.close();
 539             }
 540         } catch (IOException e) {
 541             NamingException ne = new NamingException();
 542             ne.setRootCause(e);
 543             throw ne;
 544         }
 545     }
 546 
 547     /**
 548       * Returns the attributes to bind given an object and its attributes.
 549       */
 550     static Attributes determineBindAttrs(
 551         char separator, Object obj, Attributes attrs, boolean cloned,
 552         Name name, Context ctx, Hashtable env)
 553         throws NamingException {
 554 
 555         // Call state factories to convert object and attrs
 556         DirStateFactory.Result res =
 557             DirectoryManager.getStateToBind(obj, name, ctx, env, attrs);
 558         obj = res.getObject();
 559         attrs = res.getAttributes();
 560 
 561         // We're only storing attributes; no further processing required
 562         if (obj == null) {
 563             return attrs;
 564         }
 565 
 566         //if object to be bound is a DirContext extract its attributes
 567         if ((attrs == null) && (obj instanceof DirContext)) {
 568             cloned = true;
 569             attrs = ((DirContext)obj).getAttributes("");
 570         }
 571 
 572         boolean ocNeedsCloning = false;
 573 
 574         // Create "objectClass" attribute
 575         Attribute objectClass;
 576         if (attrs == null || attrs.size() == 0) {
 577             attrs = new BasicAttributes(LdapClient.caseIgnore);
 578             cloned = true;
 579 
 580             // No objectclasses supplied, use "top" to start
 581             objectClass = new BasicAttribute("objectClass", "top");
 582 
 583         } else {
 584             // Get existing objectclass attribute
 585             objectClass = (Attribute)attrs.get("objectClass");
 586             if (objectClass == null && !attrs.isCaseIgnored()) {
 587                 // %%% workaround
 588                 objectClass = (Attribute)attrs.get("objectclass");
 589             }
 590 
 591             // No objectclasses supplied, use "top" to start
 592             if (objectClass == null) {
 593                 objectClass =  new BasicAttribute("objectClass", "top");
 594             } else if (ocNeedsCloning || !cloned) {
 595                 objectClass = (Attribute)objectClass.clone();
 596             }
 597         }
 598 
 599         // convert the supplied object into LDAP attributes
 600         attrs = encodeObject(separator, obj, attrs, objectClass, cloned);
 601 
 602         // System.err.println("Determined: " + attrs);
 603         return attrs;
 604     }
 605 
 606     /**
 607      * An ObjectInputStream that uses a class loader to find classes.
 608      */
 609     private static final class LoaderInputStream extends ObjectInputStream {
 610         private ClassLoader classLoader;
 611 
 612         LoaderInputStream(InputStream in, ClassLoader cl) throws IOException {
 613             super(in);
 614             classLoader = cl;
 615         }
 616 
 617         protected Class resolveClass(ObjectStreamClass desc) throws IOException,
 618             ClassNotFoundException {
 619             try {
 620                 // %%% Should use Class.forName(desc.getName(), false, classLoader);
 621                 // except we can't because that is only available on JDK1.2
 622                 return classLoader.loadClass(desc.getName());
 623             } catch (ClassNotFoundException e) {
 624                 return super.resolveClass(desc);
 625             }
 626         }
 627 
 628          protected Class resolveProxyClass(String[] interfaces) throws
 629                 IOException, ClassNotFoundException {
 630              ClassLoader nonPublicLoader = null;
 631              boolean hasNonPublicInterface = false;
 632 
 633              // define proxy in class loader of non-public interface(s), if any
 634              Class[] classObjs = new Class[interfaces.length];
 635              for (int i = 0; i < interfaces.length; i++) {
 636                  Class cl = Class.forName(interfaces[i], false, classLoader);
 637                  if ((cl.getModifiers() & Modifier.PUBLIC) == 0) {
 638                      if (hasNonPublicInterface) {
 639                          if (nonPublicLoader != cl.getClassLoader()) {
 640                              throw new IllegalAccessError(
 641                                 "conflicting non-public interface class loaders");
 642                          }
 643                      } else {
 644                          nonPublicLoader = cl.getClassLoader();
 645                          hasNonPublicInterface = true;
 646                      }
 647                  }
 648                  classObjs[i] = cl;
 649              }
 650              try {
 651                  return Proxy.getProxyClass(hasNonPublicInterface ?
 652                         nonPublicLoader : classLoader, classObjs);
 653              } catch (IllegalArgumentException e) {
 654                  throw new ClassNotFoundException(null, e);
 655              }
 656          }
 657 
 658      }
 659 }