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