| /* | |
| * Copyright (c) 1999, 2024, Oracle and/or its affiliates. All rights reserved. | |
| * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. | |
| * | |
| * This code is free software; you can redistribute it and/or modify it | |
| * under the terms of the GNU General Public License version 2 only, as | |
| * published by the Free Software Foundation. Oracle designates this | |
| * particular file as subject to the "Classpath" exception as provided | |
| * by Oracle in the LICENSE file that accompanied this code. | |
| * | |
| * This code is distributed in the hope that it will be useful, but WITHOUT | |
| * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | |
| * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License | |
| * version 2 for more details (a copy is included in the LICENSE file that | |
| * accompanied this code). | |
| * | |
| * You should have received a copy of the GNU General Public License version | |
| * 2 along with this work; if not, write to the Free Software Foundation, | |
| * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. | |
| * | |
| * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA | |
| * or visit www.oracle.com if you need additional information or have any | |
| * questions. | |
| */ | |
| package com.sun.jndi.ldap; | |
| import javax.naming.*; | |
| import javax.naming.directory.*; | |
| import javax.naming.spi.DirectoryManager; | |
| import javax.naming.spi.DirStateFactory; | |
| import java.io.IOException; | |
| import java.io.ByteArrayInputStream; | |
| import java.io.ByteArrayOutputStream; | |
| import java.io.ObjectInputStream; | |
| import java.io.ObjectOutputStream; | |
| import java.io.ObjectStreamClass; | |
| import java.io.InputStream; | |
| import java.util.ArrayList; | |
| import java.util.Base64; | |
| import java.util.Hashtable; | |
| import java.util.StringTokenizer; | |
| import java.lang.reflect.Proxy; | |
| import java.lang.reflect.Modifier; | |
| /** | |
| * Class containing static methods and constants for dealing with | |
| * encoding/decoding JNDI References and Serialized Objects | |
| * in LDAP. | |
| * @author Vincent Ryan | |
| * @author Rosanna Lee | |
| */ | |
| final class Obj { | |
| private Obj () {}; // Make sure no one can create one | |
| /** | |
| * Determines whether objects may be deserialized or reconstructed from a content of | |
| * 'javaSerializedData', 'javaRemoteLocation' or 'javaReferenceAddress' LDAP attributes. | |
| */ | |
| private static final boolean trustSerialData; | |
| static { | |
| // System property to control whether classes are allowed to be loaded from | |
| // 'javaSerializedData', 'javaRemoteLocation' or 'javaReferenceAddress' attributes. | |
| String trustSerialDataSp = System.getProperty( | |
| "com.sun.jndi.ldap.object.trustSerialData", "false"); | |
| trustSerialData = "true".equalsIgnoreCase(trustSerialDataSp); | |
| } | |
| // LDAP attributes used to support Java objects. | |
| static final String[] JAVA_ATTRIBUTES = { | |
| "objectClass", | |
| "javaSerializedData", | |
| "javaClassName", | |
| "javaFactory", | |
| "javaCodeBase", | |
| "javaReferenceAddress", | |
| "javaClassNames", | |
| "javaRemoteLocation" // Deprecated | |
| }; | |
| static final int OBJECT_CLASS = 0; | |
| static final int SERIALIZED_DATA = 1; | |
| static final int CLASSNAME = 2; | |
| static final int FACTORY = 3; | |
| static final int CODEBASE = 4; | |
| static final int REF_ADDR = 5; | |
| static final int TYPENAME = 6; | |
| /** | |
| * @deprecated | |
| */ | |
| @Deprecated | |
| private static final int REMOTE_LOC = 7; | |
| // LDAP object classes to support Java objects | |
| static final String[] JAVA_OBJECT_CLASSES = { | |
| "javaContainer", | |
| "javaObject", | |
| "javaNamingReference", | |
| "javaSerializedObject", | |
| "javaMarshalledObject", | |
| }; | |
| static final String[] JAVA_OBJECT_CLASSES_LOWER = { | |
| "javacontainer", | |
| "javaobject", | |
| "javanamingreference", | |
| "javaserializedobject", | |
| "javamarshalledobject", | |
| }; | |
| static final int STRUCTURAL = 0; // structural object class | |
| static final int BASE_OBJECT = 1; // auxiliary java object class | |
| static final int REF_OBJECT = 2; // auxiliary reference object class | |
| static final int SER_OBJECT = 3; // auxiliary serialized object class | |
| static final int MAR_OBJECT = 4; // auxiliary marshalled object class | |
| /** | |
| * Encode an object in LDAP attributes. | |
| * Supports binding Referenceable or Reference, Serializable, | |
| * and DirContext. | |
| * | |
| * If the object supports the Referenceable interface then encode | |
| * the reference to the object. See encodeReference() for details. | |
| *<p> | |
| * If the object is serializable, it is stored as follows: | |
| * javaClassName | |
| * value: Object.getClass(); | |
| * javaSerializedData | |
| * value: serialized form of Object (in binary form). | |
| * javaTypeName | |
| * value: getTypeNames(Object.getClass()); | |
| */ | |
| private static Attributes encodeObject(char separator, | |
| Object obj, Attributes attrs, | |
| Attribute objectClass, boolean cloned) | |
| throws NamingException { | |
| boolean structural = | |
| (objectClass.size() == 0 || | |
| (objectClass.size() == 1 && objectClass.contains("top"))); | |
| if (structural) { | |
| objectClass.add(JAVA_OBJECT_CLASSES[STRUCTURAL]); | |
| } | |
| // References | |
| if (obj instanceof Referenceable) { | |
| objectClass.add(JAVA_OBJECT_CLASSES[BASE_OBJECT]); | |
| objectClass.add(JAVA_OBJECT_CLASSES[REF_OBJECT]); | |
| if (!cloned) { | |
| attrs = (Attributes)attrs.clone(); | |
| } | |
| attrs.put(objectClass); | |
| return (encodeReference(separator, | |
| ((Referenceable)obj).getReference(), | |
| attrs, obj)); | |
| } else if (obj instanceof Reference) { | |
| objectClass.add(JAVA_OBJECT_CLASSES[BASE_OBJECT]); | |
| objectClass.add(JAVA_OBJECT_CLASSES[REF_OBJECT]); | |
| if (!cloned) { | |
| attrs = (Attributes)attrs.clone(); | |
| } | |
| attrs.put(objectClass); | |
| return (encodeReference(separator, (Reference)obj, attrs, null)); | |
| // Serializable Object | |
| } else if (obj instanceof java.io.Serializable) { | |
| objectClass.add(JAVA_OBJECT_CLASSES[BASE_OBJECT]); | |
| if (!(objectClass.contains(JAVA_OBJECT_CLASSES[MAR_OBJECT]) || | |
| objectClass.contains(JAVA_OBJECT_CLASSES_LOWER[MAR_OBJECT]))) { | |
| objectClass.add(JAVA_OBJECT_CLASSES[SER_OBJECT]); | |
| } | |
| if (!cloned) { | |
| attrs = (Attributes)attrs.clone(); | |
| } | |
| attrs.put(objectClass); | |
| attrs.put(new BasicAttribute(JAVA_ATTRIBUTES[SERIALIZED_DATA], | |
| serializeObject(obj))); | |
| if (attrs.get(JAVA_ATTRIBUTES[CLASSNAME]) == null) { | |
| attrs.put(JAVA_ATTRIBUTES[CLASSNAME], | |
| obj.getClass().getName()); | |
| } | |
| if (attrs.get(JAVA_ATTRIBUTES[TYPENAME]) == null) { | |
| Attribute tAttr = | |
| LdapCtxFactory.createTypeNameAttr(obj.getClass()); | |
| if (tAttr != null) { | |
| attrs.put(tAttr); | |
| } | |
| } | |
| // DirContext Object | |
| } else if (obj instanceof DirContext) { | |
| // do nothing | |
| } else { | |
| throw new IllegalArgumentException( | |
| "can only bind Referenceable, Serializable, DirContext"); | |
| } | |
| // System.err.println(attrs); | |
| return attrs; | |
| } | |
| /** | |
| * Each value in javaCodebase contains a list of space-separated | |
| * URLs. Each value is independent; we can pick any of the values | |
| * so we just use the first one. | |
| * @return an array of URL strings for the codebase | |
| */ | |
| private static String[] getCodebases(Attribute codebaseAttr) throws | |
| NamingException { | |
| if (codebaseAttr == null) { | |
| return null; | |
| } else { | |
| StringTokenizer parser = | |
| new StringTokenizer((String)codebaseAttr.get()); | |
| ArrayList<String> list = new ArrayList<>(10); | |
| while (parser.hasMoreTokens()) { | |
| list.add(parser.nextToken()); | |
| } | |
| String[] answer = new String[list.size()]; | |
| for (int i = 0; i < answer.length; i++) { | |
| answer[i] = list.get(i); | |
| } | |
| return answer; | |
| } | |
| } | |
| /* | |
| * Decode an object from LDAP attribute(s). | |
| * The object may be a Reference, or a Serialized object. | |
| * | |
| * See encodeObject() and encodeReference() for details on formats | |
| * expected. | |
| */ | |
| static Object decodeObject(Attributes attrs) | |
| throws NamingException { | |
| Attribute attr; | |
| // Get codebase, which is used in all 3 cases. | |
| String[] codebases = getCodebases(attrs.get(JAVA_ATTRIBUTES[CODEBASE])); | |
| try { | |
| if ((attr = attrs.get(JAVA_ATTRIBUTES[SERIALIZED_DATA])) != null) { | |
| if (!trustSerialData) { | |
| throw new NamingException("Object deserialization is not allowed"); | |
| } | |
| ClassLoader cl = Thread.currentThread().getContextClassLoader(); | |
| return deserializeObject((byte[])attr.get(), cl); | |
| } else if ((attr = attrs.get(JAVA_ATTRIBUTES[REMOTE_LOC])) != null) { | |
| // javaRemoteLocation attribute (RMI stub will be created) | |
| if (!trustSerialData) { | |
| throw new NamingException("Object deserialization is not allowed"); | |
| } | |
| // For backward compatibility only | |
| return decodeRmiObject( | |
| (String)attrs.get(JAVA_ATTRIBUTES[CLASSNAME]).get(), | |
| (String)attr.get()); | |
| } | |
| attr = attrs.get(JAVA_ATTRIBUTES[OBJECT_CLASS]); | |
| if (attr != null && | |
| (attr.contains(JAVA_OBJECT_CLASSES[REF_OBJECT]) || | |
| attr.contains(JAVA_OBJECT_CLASSES_LOWER[REF_OBJECT]))) { | |
| return decodeReference(attrs, codebases); | |
| } | |
| return null; | |
| } catch (IOException e) { | |
| NamingException ne = new NamingException(); | |
| ne.setRootCause(e); | |
| throw ne; | |
| } | |
| } | |
| /** | |
| * Convert a Reference object into several LDAP attributes. | |
| * | |
| * A Reference is stored as into the following attributes: | |
| * javaClassName | |
| * value: Reference.getClassName(); | |
| * javaFactory | |
| * value: Reference.getFactoryClassName(); | |
| * javaCodeBase | |
| * value: Reference.getFactoryClassLocation(); | |
| * javaReferenceAddress | |
| * value: #0#typeA#valA | |
| * value: #1#typeB#valB | |
| * value: #2#typeC##[serialized RefAddr C] | |
| * value: #3#typeD#valD | |
| * | |
| * where | |
| * - the first character denotes the separator | |
| * - the number following the first separator denotes the position | |
| * of the RefAddr within the Reference | |
| * - "typeA" is RefAddr.getType() | |
| * - ## denotes that the Base64-encoded form of the non-StringRefAddr | |
| * is to follow; otherwise the value that follows is | |
| * StringRefAddr.getContents() | |
| * | |
| * The default separator is the hash character (#). | |
| * May provide property for this in future. | |
| */ | |
| private static Attributes encodeReference(char separator, | |
| Reference ref, Attributes attrs, Object orig) | |
| throws NamingException { | |
| if (ref == null) | |
| return attrs; | |
| String s; | |
| if ((s = ref.getClassName()) != null) { | |
| attrs.put(new BasicAttribute(JAVA_ATTRIBUTES[CLASSNAME], s)); | |
| } | |
| if ((s = ref.getFactoryClassName()) != null) { | |
| attrs.put(new BasicAttribute(JAVA_ATTRIBUTES[FACTORY], s)); | |
| } | |
| if ((s = ref.getFactoryClassLocation()) != null) { | |
| attrs.put(new BasicAttribute(JAVA_ATTRIBUTES[CODEBASE], s)); | |
| } | |
| // Get original object's types if caller has not explicitly | |
| // specified other type names | |
| if (orig != null && attrs.get(JAVA_ATTRIBUTES[TYPENAME]) != null) { | |
| Attribute tAttr = | |
| LdapCtxFactory.createTypeNameAttr(orig.getClass()); | |
| if (tAttr != null) { | |
| attrs.put(tAttr); | |
| } | |
| } | |
| int count = ref.size(); | |
| if (count > 0) { | |
| Attribute refAttr = new BasicAttribute(JAVA_ATTRIBUTES[REF_ADDR]); | |
| RefAddr refAddr; | |
| Base64.Encoder encoder = null; | |
| for (int i = 0; i < count; i++) { | |
| refAddr = ref.get(i); | |
| if (refAddr instanceof StringRefAddr) { | |
| refAttr.add(""+ separator + i + | |
| separator + refAddr.getType() + | |
| separator + refAddr.getContent()); | |
| } else { | |
| if (encoder == null) | |
| encoder = Base64.getMimeEncoder(); | |
| refAttr.add(""+ separator + i + | |
| separator + refAddr.getType() + | |
| separator + separator + | |
| encoder.encodeToString(serializeObject(refAddr))); | |
| } | |
| } | |
| attrs.put(refAttr); | |
| } | |
| return attrs; | |
| } | |
| /* | |
| * A RMI object is stored in the directory as | |
| * javaClassName | |
| * value: Object.getClass(); | |
| * javaRemoteLocation | |
| * value: URL of RMI object (accessed through the RMI Registry) | |
| * javaCodebase: | |
| * value: URL of codebase of where to find classes for object | |
| * | |
| * Return the RMI Location URL itself. This will be turned into | |
| * an RMI object when getObjectInstance() is called on it. | |
| * %%% Ignore codebase for now. Depend on RMI registry to send code.-RL | |
| * @deprecated For backward compatibility only | |
| */ | |
| private static Object decodeRmiObject(String className, | |
| String rmiName) throws NamingException { | |
| return new Reference(className, new StringRefAddr("URL", rmiName)); | |
| } | |
| /* | |
| * Restore a Reference object from several LDAP attributes | |
| */ | |
| private static Reference decodeReference(Attributes attrs, | |
| String[] codebases) throws NamingException, IOException { | |
| Attribute attr; | |
| String className; | |
| String factory = null; | |
| if ((attr = attrs.get(JAVA_ATTRIBUTES[CLASSNAME])) != null) { | |
| className = (String)attr.get(); | |
| } else { | |
| throw new InvalidAttributesException(JAVA_ATTRIBUTES[CLASSNAME] + | |
| " attribute is required"); | |
| } | |
| if ((attr = attrs.get(JAVA_ATTRIBUTES[FACTORY])) != null) { | |
| factory = (String)attr.get(); | |
| } | |
| Reference ref = new Reference(className, factory, | |
| (codebases != null? codebases[0] : null)); | |
| /* | |
| * string encoding of a RefAddr is either: | |
| * | |
| * #posn#<type>#<address> | |
| * or | |
| * #posn#<type>##<base64-encoded address> | |
| */ | |
| if ((attr = attrs.get(JAVA_ATTRIBUTES[REF_ADDR])) != null) { | |
| String val, posnStr, type; | |
| char separator; | |
| int start, sep, posn; | |
| Base64.Decoder decoder = null; | |
| ClassLoader cl = Thread.currentThread().getContextClassLoader(); | |
| /* | |
| * Temporary array for decoded RefAddr addresses - used to ensure | |
| * unordered addresses are correctly re-ordered. | |
| */ | |
| RefAddr[] refAddrList = new RefAddr[attr.size()]; | |
| for (NamingEnumeration<?> vals = attr.getAll(); vals.hasMore(); ) { | |
| val = (String)vals.next(); | |
| if (val.length() == 0) { | |
| throw new InvalidAttributeValueException( | |
| "malformed " + JAVA_ATTRIBUTES[REF_ADDR] + " attribute - "+ | |
| "empty attribute value"); | |
| } | |
| // first character denotes encoding separator | |
| separator = val.charAt(0); | |
| start = 1; // skip over separator | |
| // extract position within Reference | |
| if ((sep = val.indexOf(separator, start)) < 0) { | |
| throw new InvalidAttributeValueException( | |
| "malformed " + JAVA_ATTRIBUTES[REF_ADDR] + " attribute - " + | |
| "separator '" + separator + "'" + "not found"); | |
| } | |
| if ((posnStr = val.substring(start, sep)) == null) { | |
| throw new InvalidAttributeValueException( | |
| "malformed " + JAVA_ATTRIBUTES[REF_ADDR] + " attribute - " + | |
| "empty RefAddr position"); | |
| } | |
| try { | |
| posn = Integer.parseInt(posnStr); | |
| } catch (NumberFormatException nfe) { | |
| throw new InvalidAttributeValueException( | |
| "malformed " + JAVA_ATTRIBUTES[REF_ADDR] + " attribute - " + | |
| "RefAddr position not an integer"); | |
| } | |
| start = sep + 1; // skip over position and trailing separator | |
| // extract type | |
| if ((sep = val.indexOf(separator, start)) < 0) { | |
| throw new InvalidAttributeValueException( | |
| "malformed " + JAVA_ATTRIBUTES[REF_ADDR] + " attribute - " + | |
| "RefAddr type not found"); | |
| } | |
| if ((type = val.substring(start, sep)) == null) { | |
| throw new InvalidAttributeValueException( | |
| "malformed " + JAVA_ATTRIBUTES[REF_ADDR] + " attribute - " + | |
| "empty RefAddr type"); | |
| } | |
| start = sep + 1; // skip over type and trailing separator | |
| // extract content | |
| if (start == val.length()) { | |
| // Empty content | |
| refAddrList[posn] = new StringRefAddr(type, null); | |
| } else if (val.charAt(start) == separator) { | |
| // Check if deserialization of binary RefAddr is allowed from | |
| // 'javaReferenceAddress' LDAP attribute. | |
| if (!trustSerialData) { | |
| throw new NamingException("Object deserialization is not allowed"); | |
| } | |
| // Double separators indicate a non-StringRefAddr | |
| // Content is a Base64-encoded serialized RefAddr | |
| ++start; // skip over consecutive separator | |
| // %%% RL: exception if empty after double separator | |
| if (decoder == null) | |
| decoder = Base64.getMimeDecoder(); | |
| RefAddr ra = (RefAddr) | |
| deserializeObject( | |
| decoder.decode(val.substring(start).getBytes()), | |
| cl); | |
| refAddrList[posn] = ra; | |
| } else { | |
| // Single separator indicates a StringRefAddr | |
| refAddrList[posn] = new StringRefAddr(type, | |
| val.substring(start)); | |
| } | |
| } | |
| // Copy to real reference | |
| for (int i = 0; i < refAddrList.length; i++) { | |
| ref.add(refAddrList[i]); | |
| } | |
| } | |
| return (ref); | |
| } | |
| /* | |
| * Serialize an object into a byte array | |
| */ | |
| private static byte[] serializeObject(Object obj) throws NamingException { | |
| try { | |
| ByteArrayOutputStream bytes = new ByteArrayOutputStream(); | |
| try (ObjectOutputStream serial = new ObjectOutputStream(bytes)) { | |
| serial.writeObject(obj); | |
| } | |
| return (bytes.toByteArray()); | |
| } catch (IOException e) { | |
| NamingException ne = new NamingException(); | |
| ne.setRootCause(e); | |
| throw ne; | |
| } | |
| } | |
| /* | |
| * Deserializes a byte array into an object. | |
| */ | |
| private static Object deserializeObject(byte[] obj, ClassLoader cl) | |
| throws NamingException { | |
| try { | |
| // Create ObjectInputStream for deserialization | |
| ByteArrayInputStream bytes = new ByteArrayInputStream(obj); | |
| try (ObjectInputStream deserial = cl == null ? | |
| new ObjectInputStream(bytes) : | |
| new LoaderInputStream(bytes, cl)) { | |
| return deserial.readObject(); | |
| } catch (ClassNotFoundException e) { | |
| NamingException ne = new NamingException(); | |
| ne.setRootCause(e); | |
| throw ne; | |
| } | |
| } catch (IOException e) { | |
| NamingException ne = new NamingException(); | |
| ne.setRootCause(e); | |
| throw ne; | |
| } | |
| } | |
| /** | |
| * Returns the attributes to bind given an object and its attributes. | |
| */ | |
| static Attributes determineBindAttrs( | |
| char separator, Object obj, Attributes attrs, boolean cloned, | |
| Name name, Context ctx, Hashtable<?,?> env) | |
| throws NamingException { | |
| // Call state factories to convert object and attrs | |
| DirStateFactory.Result res = | |
| DirectoryManager.getStateToBind(obj, name, ctx, env, attrs); | |
| obj = res.getObject(); | |
| attrs = res.getAttributes(); | |
| // We're only storing attributes; no further processing required | |
| if (obj == null) { | |
| return attrs; | |
| } | |
| //if object to be bound is a DirContext extract its attributes | |
| if ((attrs == null) && (obj instanceof DirContext)) { | |
| cloned = true; | |
| attrs = ((DirContext)obj).getAttributes(""); | |
| } | |
| boolean ocNeedsCloning = false; | |
| // Create "objectClass" attribute | |
| Attribute objectClass; | |
| if (attrs == null || attrs.size() == 0) { | |
| attrs = new BasicAttributes(LdapClient.caseIgnore); | |
| cloned = true; | |
| // No objectclasses supplied, use "top" to start | |
| objectClass = new BasicAttribute("objectClass", "top"); | |
| } else { | |
| // Get existing objectclass attribute | |
| objectClass = attrs.get("objectClass"); | |
| if (objectClass == null && !attrs.isCaseIgnored()) { | |
| // %%% workaround | |
| objectClass = attrs.get("objectclass"); | |
| } | |
| // No objectclasses supplied, use "top" to start | |
| if (objectClass == null) { | |
| objectClass = new BasicAttribute("objectClass", "top"); | |
| } else if (ocNeedsCloning || !cloned) { | |
| objectClass = (Attribute)objectClass.clone(); | |
| } | |
| } | |
| // convert the supplied object into LDAP attributes | |
| attrs = encodeObject(separator, obj, attrs, objectClass, cloned); | |
| // System.err.println("Determined: " + attrs); | |
| return attrs; | |
| } | |
| /** | |
| * An ObjectInputStream that uses a class loader to find classes. | |
| */ | |
| private static final class LoaderInputStream extends ObjectInputStream { | |
| private ClassLoader classLoader; | |
| LoaderInputStream(InputStream in, ClassLoader cl) throws IOException { | |
| super(in); | |
| classLoader = cl; | |
| } | |
| protected Class<?> resolveClass(ObjectStreamClass desc) throws | |
| IOException, ClassNotFoundException { | |
| try { | |
| // %%% Should use Class.forName(desc.getName(), false, classLoader); | |
| // except we can't because that is only available on JDK1.2 | |
| return classLoader.loadClass(desc.getName()); | |
| } catch (ClassNotFoundException e) { | |
| return super.resolveClass(desc); | |
| } | |
| } | |
| protected Class<?> resolveProxyClass(String[] interfaces) throws | |
| IOException, ClassNotFoundException { | |
| ClassLoader nonPublicLoader = null; | |
| boolean hasNonPublicInterface = false; | |
| // define proxy in class loader of non-public interface(s), if any | |
| Class<?>[] classObjs = new Class<?>[interfaces.length]; | |
| for (int i = 0; i < interfaces.length; i++) { | |
| Class<?> cl = Class.forName(interfaces[i], false, classLoader); | |
| if ((cl.getModifiers() & Modifier.PUBLIC) == 0) { | |
| if (hasNonPublicInterface) { | |
| if (nonPublicLoader != cl.getClassLoader()) { | |
| throw new IllegalAccessError( | |
| "conflicting non-public interface class loaders"); | |
| } | |
| } else { | |
| nonPublicLoader = cl.getClassLoader(); | |
| hasNonPublicInterface = true; | |
| } | |
| } | |
| classObjs[i] = cl; | |
| } | |
| try { | |
| @SuppressWarnings("deprecation") | |
| Class<?> proxyClass = Proxy.getProxyClass(hasNonPublicInterface ? | |
| nonPublicLoader : classLoader, classObjs); | |
| return proxyClass; | |
| } catch (IllegalArgumentException e) { | |
| throw new ClassNotFoundException(null, e); | |
| } | |
| } | |
| } | |
| } |