8338536: Permanently disable remote code downloading in JNDI
Reviewed-by: dfuchs
This commit is contained in:
parent
7709d435d0
commit
cee74f9e67
@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (c) 1999, 2023, Oracle and/or its affiliates. All rights reserved.
|
* Copyright (c) 1999, 2024, Oracle and/or its affiliates. All rights reserved.
|
||||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||||
*
|
*
|
||||||
* This code is free software; you can redistribute it and/or modify it
|
* This code is free software; you can redistribute it and/or modify it
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (c) 1999, 2011, Oracle and/or its affiliates. All rights reserved.
|
* Copyright (c) 1999, 2024, Oracle and/or its affiliates. All rights reserved.
|
||||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||||
*
|
*
|
||||||
* This code is free software; you can redistribute it and/or modify it
|
* This code is free software; you can redistribute it and/or modify it
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (c) 1999, 2022, Oracle and/or its affiliates. All rights reserved.
|
* Copyright (c) 1999, 2024, Oracle and/or its affiliates. All rights reserved.
|
||||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||||
*
|
*
|
||||||
* This code is free software; you can redistribute it and/or modify it
|
* This code is free software; you can redistribute it and/or modify it
|
||||||
@ -236,7 +236,7 @@ final class Obj {
|
|||||||
if (!VersionHelper.isSerialDataAllowed()) {
|
if (!VersionHelper.isSerialDataAllowed()) {
|
||||||
throw new NamingException("Object deserialization is not allowed");
|
throw new NamingException("Object deserialization is not allowed");
|
||||||
}
|
}
|
||||||
ClassLoader cl = helper.getURLClassLoader(codebases);
|
ClassLoader cl = Thread.currentThread().getContextClassLoader();
|
||||||
return deserializeObject((byte[])attr.get(), cl);
|
return deserializeObject((byte[])attr.get(), cl);
|
||||||
} else if ((attr = attrs.get(JAVA_ATTRIBUTES[REMOTE_LOC])) != null) {
|
} else if ((attr = attrs.get(JAVA_ATTRIBUTES[REMOTE_LOC])) != null) {
|
||||||
// javaRemoteLocation attribute (RMI stub will be created)
|
// javaRemoteLocation attribute (RMI stub will be created)
|
||||||
@ -246,7 +246,7 @@ final class Obj {
|
|||||||
// For backward compatibility only
|
// For backward compatibility only
|
||||||
return decodeRmiObject(
|
return decodeRmiObject(
|
||||||
(String)attrs.get(JAVA_ATTRIBUTES[CLASSNAME]).get(),
|
(String)attrs.get(JAVA_ATTRIBUTES[CLASSNAME]).get(),
|
||||||
(String)attr.get(), codebases);
|
(String)attr.get());
|
||||||
}
|
}
|
||||||
|
|
||||||
attr = attrs.get(JAVA_ATTRIBUTES[OBJECT_CLASS]);
|
attr = attrs.get(JAVA_ATTRIBUTES[OBJECT_CLASS]);
|
||||||
@ -368,7 +368,7 @@ final class Obj {
|
|||||||
* @deprecated For backward compatibility only
|
* @deprecated For backward compatibility only
|
||||||
*/
|
*/
|
||||||
private static Object decodeRmiObject(String className,
|
private static Object decodeRmiObject(String className,
|
||||||
String rmiName, String[] codebases) throws NamingException {
|
String rmiName) throws NamingException {
|
||||||
return new Reference(className, new StringRefAddr("URL", rmiName));
|
return new Reference(className, new StringRefAddr("URL", rmiName));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -410,7 +410,7 @@ final class Obj {
|
|||||||
int start, sep, posn;
|
int start, sep, posn;
|
||||||
Base64.Decoder decoder = null;
|
Base64.Decoder decoder = null;
|
||||||
|
|
||||||
ClassLoader cl = helper.getURLClassLoader(codebases);
|
ClassLoader cl = Thread.currentThread().getContextClassLoader();
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Temporary array for decoded RefAddr addresses - used to ensure
|
* Temporary array for decoded RefAddr addresses - used to ensure
|
||||||
|
@ -25,22 +25,10 @@
|
|||||||
|
|
||||||
package com.sun.jndi.ldap;
|
package com.sun.jndi.ldap;
|
||||||
|
|
||||||
import java.net.MalformedURLException;
|
|
||||||
import java.net.URL;
|
|
||||||
import java.net.URLClassLoader;
|
|
||||||
import java.security.AccessControlContext;
|
|
||||||
import java.security.AccessController;
|
|
||||||
import java.security.PrivilegedAction;
|
|
||||||
|
|
||||||
public final class VersionHelper {
|
public final class VersionHelper {
|
||||||
|
|
||||||
private static final VersionHelper helper = new VersionHelper();
|
private static final VersionHelper helper = new VersionHelper();
|
||||||
|
|
||||||
/**
|
|
||||||
* Determines whether classes may be loaded from an arbitrary URL code base.
|
|
||||||
*/
|
|
||||||
private static final boolean trustURLCodebase;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Determines whether objects may be deserialized or reconstructed from a content of
|
* Determines whether objects may be deserialized or reconstructed from a content of
|
||||||
* 'javaSerializedData', 'javaRemoteLocation' or 'javaReferenceAddress' LDAP attributes.
|
* 'javaSerializedData', 'javaRemoteLocation' or 'javaReferenceAddress' LDAP attributes.
|
||||||
@ -48,29 +36,13 @@ public final class VersionHelper {
|
|||||||
private static final boolean trustSerialData;
|
private static final boolean trustSerialData;
|
||||||
|
|
||||||
static {
|
static {
|
||||||
// System property to control whether classes may be loaded from an
|
|
||||||
// arbitrary URL code base
|
|
||||||
String trust = getPrivilegedProperty(
|
|
||||||
"com.sun.jndi.ldap.object.trustURLCodebase", "false");
|
|
||||||
trustURLCodebase = "true".equalsIgnoreCase(trust);
|
|
||||||
|
|
||||||
// System property to control whether classes are allowed to be loaded from
|
// System property to control whether classes are allowed to be loaded from
|
||||||
// 'javaSerializedData', 'javaRemoteLocation' or 'javaReferenceAddress' attributes.
|
// 'javaSerializedData', 'javaRemoteLocation' or 'javaReferenceAddress' attributes.
|
||||||
String trustSerialDataSp = getPrivilegedProperty(
|
String trustSerialDataSp = System.getProperty(
|
||||||
"com.sun.jndi.ldap.object.trustSerialData", "false");
|
"com.sun.jndi.ldap.object.trustSerialData", "false");
|
||||||
trustSerialData = "true".equalsIgnoreCase(trustSerialDataSp);
|
trustSerialData = "true".equalsIgnoreCase(trustSerialDataSp);
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressWarnings("removal")
|
|
||||||
private static String getPrivilegedProperty(String propertyName, String defaultVal) {
|
|
||||||
PrivilegedAction<String> action = () -> System.getProperty(propertyName, defaultVal);
|
|
||||||
if (System.getSecurityManager() == null) {
|
|
||||||
return action.run();
|
|
||||||
} else {
|
|
||||||
return AccessController.doPrivileged(action);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private VersionHelper() {
|
private VersionHelper() {
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -89,41 +61,12 @@ public final class VersionHelper {
|
|||||||
return trustSerialData;
|
return trustSerialData;
|
||||||
}
|
}
|
||||||
|
|
||||||
ClassLoader getURLClassLoader(String[] url) throws MalformedURLException {
|
|
||||||
ClassLoader parent = getContextClassLoader();
|
|
||||||
/*
|
|
||||||
* Classes may only be loaded from an arbitrary URL code base when
|
|
||||||
* the system property com.sun.jndi.ldap.object.trustURLCodebase
|
|
||||||
* has been set to "true".
|
|
||||||
*/
|
|
||||||
if (url != null && trustURLCodebase) {
|
|
||||||
return URLClassLoader.newInstance(getUrlArray(url), parent);
|
|
||||||
} else {
|
|
||||||
return parent;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Class<?> loadClass(String className) throws ClassNotFoundException {
|
Class<?> loadClass(String className) throws ClassNotFoundException {
|
||||||
return Class.forName(className, true, getContextClassLoader());
|
return Class.forName(className, true,
|
||||||
|
Thread.currentThread().getContextClassLoader());
|
||||||
}
|
}
|
||||||
|
|
||||||
Thread createThread(Runnable r) {
|
Thread createThread(Runnable r) {
|
||||||
return new Thread(r);
|
return new Thread(r);
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressWarnings("removal")
|
|
||||||
private ClassLoader getContextClassLoader() {
|
|
||||||
PrivilegedAction<ClassLoader> act =
|
|
||||||
Thread.currentThread()::getContextClassLoader;
|
|
||||||
return AccessController.doPrivileged(act);
|
|
||||||
}
|
|
||||||
|
|
||||||
@SuppressWarnings("deprecation")
|
|
||||||
private static URL[] getUrlArray(String[] url) throws MalformedURLException {
|
|
||||||
URL[] urlArray = new URL[url.length];
|
|
||||||
for (int i = 0; i < urlArray.length; i++) {
|
|
||||||
urlArray[i] = new URL(url[i]);
|
|
||||||
}
|
|
||||||
return urlArray;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved.
|
* Copyright (c) 2022, 2024, Oracle and/or its affiliates. All rights reserved.
|
||||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||||
*
|
*
|
||||||
* This code is free software; you can redistribute it and/or modify it
|
* This code is free software; you can redistribute it and/or modify it
|
||||||
@ -181,9 +181,8 @@ public class NamingManagerHelper {
|
|||||||
static ObjectFactory getObjectFactoryFromReference(
|
static ObjectFactory getObjectFactoryFromReference(
|
||||||
Reference ref, String factoryName, Predicate<Class<?>> filter)
|
Reference ref, String factoryName, Predicate<Class<?>> filter)
|
||||||
throws IllegalAccessException,
|
throws IllegalAccessException,
|
||||||
InstantiationException,
|
InstantiationException {
|
||||||
MalformedURLException {
|
Class<?> clas;
|
||||||
Class<?> clas = null;
|
|
||||||
|
|
||||||
// Try to use current class loader
|
// Try to use current class loader
|
||||||
try {
|
try {
|
||||||
@ -193,27 +192,11 @@ public class NamingManagerHelper {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
} catch (ClassNotFoundException e) {
|
} catch (ClassNotFoundException e) {
|
||||||
// ignore and continue
|
return null;
|
||||||
// e.printStackTrace();
|
|
||||||
}
|
}
|
||||||
// All other exceptions are passed up.
|
assert clas != null;
|
||||||
|
|
||||||
// Not in class path; try to use codebase
|
|
||||||
String codebase;
|
|
||||||
if (clas == null &&
|
|
||||||
(codebase = ref.getFactoryClassLocation()) != null) {
|
|
||||||
try {
|
|
||||||
clas = helper.loadClass(factoryName, codebase);
|
|
||||||
// Validate factory's class with the objects factory serial filter
|
|
||||||
if (clas == null || !filter.test(clas)) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
} catch (ClassNotFoundException e) {
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@SuppressWarnings("deprecation") // Class.newInstance
|
@SuppressWarnings("deprecation") // Class.newInstance
|
||||||
ObjectFactory result = (clas != null) ? (ObjectFactory) clas.newInstance() : null;
|
ObjectFactory result = (ObjectFactory) clas.newInstance();
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -401,12 +384,6 @@ public class NamingManagerHelper {
|
|||||||
ObjectFactoryBuilder builder) throws NamingException {
|
ObjectFactoryBuilder builder) throws NamingException {
|
||||||
if (object_factory_builder != null)
|
if (object_factory_builder != null)
|
||||||
throw new IllegalStateException("ObjectFactoryBuilder already set");
|
throw new IllegalStateException("ObjectFactoryBuilder already set");
|
||||||
|
|
||||||
@SuppressWarnings("removal")
|
|
||||||
SecurityManager security = System.getSecurityManager();
|
|
||||||
if (security != null) {
|
|
||||||
security.checkSetFactory();
|
|
||||||
}
|
|
||||||
object_factory_builder = builder;
|
object_factory_builder = builder;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (c) 1999, 2022, Oracle and/or its affiliates. All rights reserved.
|
* Copyright (c) 1999, 2024, Oracle and/or its affiliates. All rights reserved.
|
||||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||||
*
|
*
|
||||||
* This code is free software; you can redistribute it and/or modify it
|
* This code is free software; you can redistribute it and/or modify it
|
||||||
@ -28,15 +28,9 @@ package com.sun.naming.internal;
|
|||||||
import javax.naming.NamingEnumeration;
|
import javax.naming.NamingEnumeration;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.net.MalformedURLException;
|
|
||||||
import java.net.URL;
|
import java.net.URL;
|
||||||
import java.net.URLClassLoader;
|
|
||||||
import java.nio.file.Files;
|
import java.nio.file.Files;
|
||||||
import java.nio.file.Path;
|
import java.nio.file.Path;
|
||||||
import java.security.AccessController;
|
|
||||||
import java.security.PrivilegedAction;
|
|
||||||
import java.security.PrivilegedActionException;
|
|
||||||
import java.security.PrivilegedExceptionAction;
|
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -53,21 +47,6 @@ import java.util.*;
|
|||||||
public final class VersionHelper {
|
public final class VersionHelper {
|
||||||
private static final VersionHelper helper = new VersionHelper();
|
private static final VersionHelper helper = new VersionHelper();
|
||||||
|
|
||||||
/**
|
|
||||||
* Determines whether classes may be loaded from an arbitrary URL code base.
|
|
||||||
*/
|
|
||||||
private static final boolean TRUST_URL_CODE_BASE;
|
|
||||||
|
|
||||||
static {
|
|
||||||
// System property to control whether classes may be loaded from an
|
|
||||||
// arbitrary URL code base
|
|
||||||
PrivilegedAction<String> act
|
|
||||||
= () -> System.getProperty("com.sun.jndi.ldap.object.trustURLCodebase", "false");
|
|
||||||
@SuppressWarnings("removal")
|
|
||||||
String trust = AccessController.doPrivileged(act);
|
|
||||||
TRUST_URL_CODE_BASE = "true".equalsIgnoreCase(trust);
|
|
||||||
}
|
|
||||||
|
|
||||||
static final String[] PROPS = new String[]{
|
static final String[] PROPS = new String[]{
|
||||||
javax.naming.Context.INITIAL_CONTEXT_FACTORY,
|
javax.naming.Context.INITIAL_CONTEXT_FACTORY,
|
||||||
javax.naming.Context.OBJECT_FACTORIES,
|
javax.naming.Context.OBJECT_FACTORIES,
|
||||||
@ -101,22 +80,6 @@ public final class VersionHelper {
|
|||||||
return loadClass(className, false, getContextClassLoader());
|
return loadClass(className, false, getContextClassLoader());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @param className A non-null fully qualified class name.
|
|
||||||
* @param codebase A non-null, space-separated list of URL strings.
|
|
||||||
*/
|
|
||||||
public Class<?> loadClass(String className, String codebase)
|
|
||||||
throws ClassNotFoundException, MalformedURLException {
|
|
||||||
if (TRUST_URL_CODE_BASE) {
|
|
||||||
ClassLoader parent = getContextClassLoader();
|
|
||||||
ClassLoader cl
|
|
||||||
= URLClassLoader.newInstance(getUrlArray(codebase), parent);
|
|
||||||
return loadClass(className, cl);
|
|
||||||
} else {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Package private.
|
* Package private.
|
||||||
* <p>
|
* <p>
|
||||||
@ -136,37 +99,19 @@ public final class VersionHelper {
|
|||||||
|
|
||||||
/*
|
/*
|
||||||
* Returns a JNDI property from the system properties. Returns
|
* Returns a JNDI property from the system properties. Returns
|
||||||
* null if the property is not set, or if there is no permission
|
* null if the property is not set.
|
||||||
* to read it.
|
|
||||||
*/
|
*/
|
||||||
@SuppressWarnings("removal")
|
|
||||||
String getJndiProperty(int i) {
|
String getJndiProperty(int i) {
|
||||||
PrivilegedAction<String> act = () -> {
|
return System.getProperty(PROPS[i]);
|
||||||
try {
|
|
||||||
return System.getProperty(PROPS[i]);
|
|
||||||
} catch (SecurityException e) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
return AccessController.doPrivileged(act);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Reads each property in PROPS from the system properties, and
|
* Reads each property in PROPS from the system properties, and
|
||||||
* returns their values -- in order -- in an array. For each
|
* returns their values -- in order -- in an array. For each
|
||||||
* unset property, the corresponding array element is set to null.
|
* unset property, the corresponding array element is set to null.
|
||||||
* Returns null if there is no permission to call System.getProperties().
|
|
||||||
*/
|
*/
|
||||||
String[] getJndiProperties() {
|
String[] getJndiProperties() {
|
||||||
PrivilegedAction<Properties> act = () -> {
|
Properties sysProps = System.getProperties();
|
||||||
try {
|
|
||||||
return System.getProperties();
|
|
||||||
} catch (SecurityException e) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
@SuppressWarnings("removal")
|
|
||||||
Properties sysProps = AccessController.doPrivileged(act);
|
|
||||||
if (sysProps == null) {
|
if (sysProps == null) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
@ -199,16 +144,12 @@ public final class VersionHelper {
|
|||||||
* Returns the resource of a given name associated with a particular
|
* Returns the resource of a given name associated with a particular
|
||||||
* class (never null), or null if none can be found.
|
* class (never null), or null if none can be found.
|
||||||
*/
|
*/
|
||||||
@SuppressWarnings("removal")
|
|
||||||
InputStream getResourceAsStream(Class<?> c, String name) {
|
InputStream getResourceAsStream(Class<?> c, String name) {
|
||||||
PrivilegedAction<InputStream> act = () -> {
|
try {
|
||||||
try {
|
return c.getModule().getResourceAsStream(resolveName(c, name));
|
||||||
return c.getModule().getResourceAsStream(resolveName(c, name));
|
} catch (IOException x) {
|
||||||
} catch (IOException x) {
|
return null;
|
||||||
return null;
|
}
|
||||||
}
|
|
||||||
};
|
|
||||||
return AccessController.doPrivileged(act);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -217,20 +158,16 @@ public final class VersionHelper {
|
|||||||
*
|
*
|
||||||
* @param filename The file name, sans directory.
|
* @param filename The file name, sans directory.
|
||||||
*/
|
*/
|
||||||
@SuppressWarnings("removal")
|
|
||||||
InputStream getJavaHomeConfStream(String filename) {
|
InputStream getJavaHomeConfStream(String filename) {
|
||||||
PrivilegedAction<InputStream> act = () -> {
|
try {
|
||||||
try {
|
String javahome = System.getProperty("java.home");
|
||||||
String javahome = System.getProperty("java.home");
|
if (javahome == null) {
|
||||||
if (javahome == null) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
return Files.newInputStream(Path.of(javahome, "conf", filename));
|
|
||||||
} catch (Exception e) {
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
};
|
return Files.newInputStream(Path.of(javahome, "conf", filename));
|
||||||
return AccessController.doPrivileged(act);
|
} catch (Exception e) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -239,19 +176,12 @@ public final class VersionHelper {
|
|||||||
* loader. Null represents the bootstrap class loader in some
|
* loader. Null represents the bootstrap class loader in some
|
||||||
* Java implementations.
|
* Java implementations.
|
||||||
*/
|
*/
|
||||||
@SuppressWarnings("removal")
|
|
||||||
NamingEnumeration<InputStream> getResources(ClassLoader cl,
|
NamingEnumeration<InputStream> getResources(ClassLoader cl,
|
||||||
String name) throws IOException {
|
String name) throws IOException {
|
||||||
Enumeration<URL> urls;
|
Enumeration<URL> urls;
|
||||||
PrivilegedExceptionAction<Enumeration<URL>> act = () ->
|
urls = (cl == null)
|
||||||
(cl == null)
|
? ClassLoader.getSystemResources(name)
|
||||||
? ClassLoader.getSystemResources(name)
|
: cl.getResources(name);
|
||||||
: cl.getResources(name);
|
|
||||||
try {
|
|
||||||
urls = AccessController.doPrivileged(act);
|
|
||||||
} catch (PrivilegedActionException e) {
|
|
||||||
throw (IOException) e.getException();
|
|
||||||
}
|
|
||||||
return new InputStreamEnumeration(urls);
|
return new InputStreamEnumeration(urls);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -265,39 +195,18 @@ public final class VersionHelper {
|
|||||||
* Please don't expose this method as public.
|
* Please don't expose this method as public.
|
||||||
* @throws SecurityException if the class loader is not accessible
|
* @throws SecurityException if the class loader is not accessible
|
||||||
*/
|
*/
|
||||||
@SuppressWarnings("removal")
|
|
||||||
ClassLoader getContextClassLoader() {
|
ClassLoader getContextClassLoader() {
|
||||||
|
ClassLoader loader = Thread.currentThread().getContextClassLoader();
|
||||||
PrivilegedAction<ClassLoader> act = () -> {
|
if (loader == null) {
|
||||||
ClassLoader loader = Thread.currentThread().getContextClassLoader();
|
// Don't use bootstrap class loader directly!
|
||||||
if (loader == null) {
|
loader = ClassLoader.getSystemClassLoader();
|
||||||
// Don't use bootstrap class loader directly!
|
|
||||||
loader = ClassLoader.getSystemClassLoader();
|
|
||||||
}
|
|
||||||
return loader;
|
|
||||||
};
|
|
||||||
return AccessController.doPrivileged(act);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static URL[] getUrlArray(String codebase)
|
|
||||||
throws MalformedURLException {
|
|
||||||
// Parse codebase into separate URLs
|
|
||||||
StringTokenizer parser = new StringTokenizer(codebase);
|
|
||||||
List<URL> list = new ArrayList<>();
|
|
||||||
while (parser.hasMoreTokens()) {
|
|
||||||
@SuppressWarnings("deprecation")
|
|
||||||
var u = new URL(parser.nextToken());
|
|
||||||
list.add(u);
|
|
||||||
}
|
}
|
||||||
return list.toArray(new URL[0]);
|
return loader;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Given an enumeration of URLs, an instance of this class represents
|
* Given an enumeration of URLs, an instance of this class represents
|
||||||
* an enumeration of their InputStreams. Each operation on the URL
|
* an enumeration of their InputStreams.
|
||||||
* enumeration is performed within a doPrivileged block.
|
|
||||||
* This is used to enumerate the resources under a foreign codebase.
|
|
||||||
* This class is not MT-safe.
|
|
||||||
*/
|
*/
|
||||||
private class InputStreamEnumeration implements
|
private class InputStreamEnumeration implements
|
||||||
NamingEnumeration<InputStream> {
|
NamingEnumeration<InputStream> {
|
||||||
@ -314,19 +223,15 @@ public final class VersionHelper {
|
|||||||
* Returns the next InputStream, or null if there are no more.
|
* Returns the next InputStream, or null if there are no more.
|
||||||
* An InputStream that cannot be opened is skipped.
|
* An InputStream that cannot be opened is skipped.
|
||||||
*/
|
*/
|
||||||
@SuppressWarnings("removal")
|
|
||||||
private InputStream getNextElement() {
|
private InputStream getNextElement() {
|
||||||
PrivilegedAction<InputStream> act = () -> {
|
while (urls.hasMoreElements()) {
|
||||||
while (urls.hasMoreElements()) {
|
try {
|
||||||
try {
|
return urls.nextElement().openStream();
|
||||||
return urls.nextElement().openStream();
|
} catch (IOException e) {
|
||||||
} catch (IOException e) {
|
// skip this URL
|
||||||
// skip this URL
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return null;
|
}
|
||||||
};
|
return null;
|
||||||
return AccessController.doPrivileged(act);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean hasMore() {
|
public boolean hasMore() {
|
||||||
|
@ -123,9 +123,12 @@ public class NamingManager {
|
|||||||
* or {@code Referenceable} containing a factory class name,
|
* or {@code Referenceable} containing a factory class name,
|
||||||
* use the named factory to create the object.
|
* use the named factory to create the object.
|
||||||
* Return {@code refInfo} if the factory cannot be created.
|
* Return {@code refInfo} if the factory cannot be created.
|
||||||
* Under JDK 1.1, if the factory class must be loaded from a location
|
* Downloading a factory class from a location specified in the reference
|
||||||
* specified in the reference, a {@code SecurityManager} must have
|
* can be supported by a custom implementation of {@link ObjectFactoryBuilder}.
|
||||||
* been installed or the factory creation will fail.
|
* The {@linkplain Reference#getFactoryClassLocation() factory class
|
||||||
|
* location}, if present, is ignored. A custom {@link ObjectFactoryBuilder}
|
||||||
|
* {@linkplain #setObjectFactoryBuilder(ObjectFactoryBuilder) may be used}
|
||||||
|
* if a different policy is desired.
|
||||||
* If an exception is encountered while creating the factory,
|
* If an exception is encountered while creating the factory,
|
||||||
* it is passed up to the caller.
|
* it is passed up to the caller.
|
||||||
* <li>If {@code refInfo} is a {@code Reference} or
|
* <li>If {@code refInfo} is a {@code Reference} or
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (c) 1999, 2022, Oracle and/or its affiliates. All rights reserved.
|
* Copyright (c) 1999, 2024, Oracle and/or its affiliates. All rights reserved.
|
||||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||||
*
|
*
|
||||||
* This code is free software; you can redistribute it and/or modify it
|
* This code is free software; you can redistribute it and/or modify it
|
||||||
@ -35,8 +35,6 @@ import java.rmi.*;
|
|||||||
import java.rmi.server.*;
|
import java.rmi.server.*;
|
||||||
import java.rmi.registry.Registry;
|
import java.rmi.registry.Registry;
|
||||||
import java.rmi.registry.LocateRegistry;
|
import java.rmi.registry.LocateRegistry;
|
||||||
import java.security.AccessController;
|
|
||||||
import java.security.PrivilegedAction;
|
|
||||||
|
|
||||||
import javax.naming.*;
|
import javax.naming.*;
|
||||||
import javax.naming.spi.NamingManager;
|
import javax.naming.spi.NamingManager;
|
||||||
@ -57,19 +55,6 @@ public class RegistryContext implements Context, Referenceable {
|
|||||||
private int port;
|
private int port;
|
||||||
private static final NameParser nameParser = new AtomicNameParser();
|
private static final NameParser nameParser = new AtomicNameParser();
|
||||||
private static final String SOCKET_FACTORY = "com.sun.jndi.rmi.factory.socket";
|
private static final String SOCKET_FACTORY = "com.sun.jndi.rmi.factory.socket";
|
||||||
/**
|
|
||||||
* Determines whether classes may be loaded from an arbitrary URL code base.
|
|
||||||
*/
|
|
||||||
static final boolean trustURLCodebase;
|
|
||||||
static {
|
|
||||||
// System property to control whether classes may be loaded from an
|
|
||||||
// arbitrary URL codebase
|
|
||||||
PrivilegedAction<String> act = () -> System.getProperty(
|
|
||||||
"com.sun.jndi.rmi.object.trustURLCodebase", "false");
|
|
||||||
@SuppressWarnings("removal")
|
|
||||||
String trust = AccessController.doPrivileged(act);
|
|
||||||
trustURLCodebase = "true".equalsIgnoreCase(trust);
|
|
||||||
}
|
|
||||||
|
|
||||||
Reference reference = null; // ref used to create this context, if any
|
Reference reference = null; // ref used to create this context, if any
|
||||||
|
|
||||||
@ -481,12 +466,6 @@ public class RegistryContext implements Context, Referenceable {
|
|||||||
? ((RemoteReference)r).getReference()
|
? ((RemoteReference)r).getReference()
|
||||||
: (Object)r;
|
: (Object)r;
|
||||||
|
|
||||||
/*
|
|
||||||
* Classes may only be loaded from an arbitrary URL codebase when
|
|
||||||
* the system property com.sun.jndi.rmi.object.trustURLCodebase
|
|
||||||
* has been set to "true".
|
|
||||||
*/
|
|
||||||
|
|
||||||
// Use reference if possible
|
// Use reference if possible
|
||||||
Reference ref = null;
|
Reference ref = null;
|
||||||
if (obj instanceof Reference) {
|
if (obj instanceof Reference) {
|
||||||
@ -495,11 +474,14 @@ public class RegistryContext implements Context, Referenceable {
|
|||||||
ref = ((Referenceable)(obj)).getReference();
|
ref = ((Referenceable)(obj)).getReference();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ref != null && ref.getFactoryClassLocation() != null &&
|
/*
|
||||||
!trustURLCodebase) {
|
* Downloading a factory class from a location specified in the reference
|
||||||
|
* can be supported by a custom implementation of "ObjectFactoryBuilder".
|
||||||
|
*/
|
||||||
|
if (NamingManagerHelper.getObjectFactoryBuilder() == null
|
||||||
|
&& ref != null && ref.getFactoryClassLocation() != null) {
|
||||||
throw new ConfigurationException(
|
throw new ConfigurationException(
|
||||||
"The object factory is untrusted. Set the system property" +
|
"Remote object factories are not supported");
|
||||||
" 'com.sun.jndi.rmi.object.trustURLCodebase' to 'true'.");
|
|
||||||
}
|
}
|
||||||
return NamingManagerHelper.getObjectInstance(obj, name, this,
|
return NamingManagerHelper.getObjectInstance(obj, name, this,
|
||||||
environment, ObjectFactoriesFilter::checkRmiFilter);
|
environment, ObjectFactoriesFilter::checkRmiFilter);
|
||||||
|
@ -58,6 +58,11 @@
|
|||||||
* implementation.
|
* implementation.
|
||||||
* </li>
|
* </li>
|
||||||
* </ul>
|
* </ul>
|
||||||
|
* <p> Downloading a factory class from a {@linkplain javax.naming.Reference#getFactoryClassLocation()
|
||||||
|
* location} specified in the reference can be supported by a custom implementation of {@link
|
||||||
|
* javax.naming.spi.ObjectFactoryBuilder}. If a location is specified, then
|
||||||
|
* unless an {@link javax.naming.spi.ObjectFactoryBuilder} is installed a
|
||||||
|
* {@link javax.naming.ConfigurationException} is thrown.
|
||||||
* @provides javax.naming.spi.InitialContextFactory
|
* @provides javax.naming.spi.InitialContextFactory
|
||||||
* @moduleGraph
|
* @moduleGraph
|
||||||
* @since 9
|
* @since 9
|
||||||
|
@ -0,0 +1,223 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 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.
|
||||||
|
*
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import com.sun.net.httpserver.HttpServer;
|
||||||
|
import com.sun.net.httpserver.SimpleFileServer;
|
||||||
|
|
||||||
|
import javax.naming.Context;
|
||||||
|
import javax.naming.InitialContext;
|
||||||
|
import javax.naming.NamingException;
|
||||||
|
import javax.naming.Reference;
|
||||||
|
import javax.naming.spi.NamingManager;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.net.InetAddress;
|
||||||
|
import java.net.InetSocketAddress;
|
||||||
|
import java.net.ServerSocket;
|
||||||
|
import java.net.Socket;
|
||||||
|
import java.net.SocketAddress;
|
||||||
|
import java.nio.file.Files;
|
||||||
|
import java.nio.file.Path;
|
||||||
|
import java.nio.file.Paths;
|
||||||
|
import java.rmi.RemoteException;
|
||||||
|
import java.rmi.registry.Registry;
|
||||||
|
import java.rmi.server.RMISocketFactory;
|
||||||
|
import java.util.Hashtable;
|
||||||
|
import java.util.Objects;
|
||||||
|
|
||||||
|
import jdk.test.lib.net.URIBuilder;
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* @test
|
||||||
|
* @bug 8338536
|
||||||
|
* @summary Check if an object factory builder can be used to reconstruct
|
||||||
|
* object factories from a code base specified in a
|
||||||
|
* @modules java.rmi/sun.rmi.registry
|
||||||
|
* java.rmi/sun.rmi.server
|
||||||
|
* java.rmi/sun.rmi.transport
|
||||||
|
* java.rmi/sun.rmi.transport.tcp
|
||||||
|
* @library /test/lib ../../../../../../java/rmi/testlibrary
|
||||||
|
* @build TestLibrary
|
||||||
|
* @compile TestFactory.java TestObjectFactoryBuilder.java
|
||||||
|
*
|
||||||
|
* @run main/othervm ObjectFactoryBuilderCodebaseTest setObjectFactoryBuilder
|
||||||
|
* @run main/othervm ObjectFactoryBuilderCodebaseTest default
|
||||||
|
*/
|
||||||
|
public class ObjectFactoryBuilderCodebaseTest {
|
||||||
|
|
||||||
|
public static void main(String[] args) throws Exception {
|
||||||
|
setupRmiHostNameAndRmiSocketFactory();
|
||||||
|
boolean useCustomObjectFactoryBuilder =
|
||||||
|
"setObjectFactoryBuilder".equals(args[0]);
|
||||||
|
|
||||||
|
if (args.length > 0 && useCustomObjectFactoryBuilder) {
|
||||||
|
NamingManager.setObjectFactoryBuilder(new TestObjectFactoryBuilder());
|
||||||
|
}
|
||||||
|
FileServer fileServer = configureAndLaunchFileServer();
|
||||||
|
int registryPort;
|
||||||
|
try {
|
||||||
|
Registry registry = TestLibrary.createRegistryOnEphemeralPort();
|
||||||
|
registryPort = TestLibrary.getRegistryPort(registry);
|
||||||
|
System.out.println("Registry port: " + registryPort);
|
||||||
|
} catch (RemoteException re) {
|
||||||
|
throw new RuntimeException("Failed to create registry", re);
|
||||||
|
}
|
||||||
|
|
||||||
|
Context context = getInitialContext(registryPort);
|
||||||
|
// Bind the Reference object
|
||||||
|
String factoryURL = fileServer.factoryLocation();
|
||||||
|
System.err.println("Setting Reference factory location: " + factoryURL);
|
||||||
|
Reference ref = new Reference("TestObject", "com.test.TestFactory",
|
||||||
|
factoryURL);
|
||||||
|
context.bind("objectTest", ref);
|
||||||
|
|
||||||
|
// Try to load bound reference
|
||||||
|
try {
|
||||||
|
Object object = context.lookup("objectTest");
|
||||||
|
if (!useCustomObjectFactoryBuilder) {
|
||||||
|
throw new RuntimeException("Lookup not expected to complete");
|
||||||
|
}
|
||||||
|
System.err.println("Loaded object: " + object);
|
||||||
|
} catch (NamingException ne) {
|
||||||
|
if (useCustomObjectFactoryBuilder) {
|
||||||
|
throw new RuntimeException("Lookup expected to complete successfully", ne);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Context getInitialContext(int port) throws NamingException {
|
||||||
|
Hashtable<String, String> env = new Hashtable<>();
|
||||||
|
|
||||||
|
// Prepare registry URL
|
||||||
|
String providerURL = URIBuilder.newBuilder()
|
||||||
|
.loopback()
|
||||||
|
.port(port)
|
||||||
|
.scheme("rmi")
|
||||||
|
.buildUnchecked().toString();
|
||||||
|
|
||||||
|
env.put(Context.INITIAL_CONTEXT_FACTORY,
|
||||||
|
"com.sun.jndi.rmi.registry.RegistryContextFactory");
|
||||||
|
env.put(Context.PROVIDER_URL, providerURL);
|
||||||
|
return new InitialContext(env);
|
||||||
|
}
|
||||||
|
|
||||||
|
private record FileServer(Path rootPath, InetSocketAddress address, HttpServer httpServer) {
|
||||||
|
|
||||||
|
public static FileServer newInstance(Path rootPath, InetSocketAddress address) {
|
||||||
|
Objects.requireNonNull(address);
|
||||||
|
Objects.requireNonNull(rootPath);
|
||||||
|
var httpServer = SimpleFileServer.createFileServer(address, rootPath,
|
||||||
|
SimpleFileServer.OutputLevel.VERBOSE);
|
||||||
|
return new FileServer(rootPath, address, httpServer);
|
||||||
|
}
|
||||||
|
|
||||||
|
String factoryLocation() {
|
||||||
|
return URIBuilder.newBuilder()
|
||||||
|
.loopback()
|
||||||
|
.port(port())
|
||||||
|
.scheme("http")
|
||||||
|
.path("/")
|
||||||
|
.buildUnchecked()
|
||||||
|
.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
int port() {
|
||||||
|
return httpServer.getAddress().getPort();
|
||||||
|
}
|
||||||
|
|
||||||
|
void start() {
|
||||||
|
httpServer.start();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Prepares and launches the file server capable of serving TestFactory.class
|
||||||
|
private static FileServer configureAndLaunchFileServer() throws IOException {
|
||||||
|
|
||||||
|
// Location of compiled classes with compiled MyFactory
|
||||||
|
Path factoryClassPath = Path.of(System.getProperty("test.classes", "."))
|
||||||
|
.resolve(OBJ_FACTORY_PACKAGE_PATH)
|
||||||
|
.resolve(OBJ_FACTORY_CLASS_NAME);
|
||||||
|
|
||||||
|
// File server content root directory
|
||||||
|
Path serverRoot = Paths.get("serverRoot").toAbsolutePath();
|
||||||
|
Path packageDirInServerRoot = serverRoot.resolve(OBJ_FACTORY_PACKAGE_PATH);
|
||||||
|
Path factoryClassFileInServerRoot = packageDirInServerRoot.resolve(OBJ_FACTORY_CLASS_NAME);
|
||||||
|
|
||||||
|
// Remove files from previous run
|
||||||
|
Files.deleteIfExists(factoryClassFileInServerRoot);
|
||||||
|
Files.deleteIfExists(packageDirInServerRoot);
|
||||||
|
Files.deleteIfExists(packageDirInServerRoot.getParent());
|
||||||
|
Files.deleteIfExists(serverRoot);
|
||||||
|
|
||||||
|
// Create server root and copy compiled object factory inside
|
||||||
|
Files.createDirectories(packageDirInServerRoot);
|
||||||
|
Files.copy(factoryClassPath, factoryClassFileInServerRoot);
|
||||||
|
|
||||||
|
// Bind file server to loopback address
|
||||||
|
InetSocketAddress serverAddress =
|
||||||
|
new InetSocketAddress(InetAddress.getLoopbackAddress(), 0);
|
||||||
|
FileServer fileServer = FileServer.newInstance(serverRoot, serverAddress);
|
||||||
|
|
||||||
|
// Start the file server
|
||||||
|
fileServer.start();
|
||||||
|
System.err.println("File server content root: " + serverRoot);
|
||||||
|
System.err.printf("File server running on %s:%d%n",
|
||||||
|
serverAddress.getAddress(), fileServer.port());
|
||||||
|
return fileServer;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Configure RMI to launch registry on a loopback address
|
||||||
|
private static void setupRmiHostNameAndRmiSocketFactory() throws IOException {
|
||||||
|
String rmiServerHostAddressString = InetAddress.getLoopbackAddress().getHostAddress();
|
||||||
|
System.out.println("Setting 'java.rmi.server.hostname' to: " + rmiServerHostAddressString);
|
||||||
|
System.setProperty("java.rmi.server.hostname", rmiServerHostAddressString);
|
||||||
|
RMISocketFactory.setSocketFactory(new TestRmiSocketFactory());
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class TestRmiSocketFactory extends RMISocketFactory {
|
||||||
|
public ServerSocket createServerSocket(int port) throws IOException {
|
||||||
|
var loopbackAddress = InetAddress.getLoopbackAddress();
|
||||||
|
System.out.printf("Creating RMI server socket on %s:%d%n", loopbackAddress, port);
|
||||||
|
ServerSocket rmiServerSocket = new ServerSocket();
|
||||||
|
rmiServerSocket.setOption(java.net.StandardSocketOptions.SO_REUSEADDR, false);
|
||||||
|
SocketAddress serverAddress = new InetSocketAddress(loopbackAddress, port);
|
||||||
|
rmiServerSocket.bind(serverAddress, BACKLOG_OF_5);
|
||||||
|
return rmiServerSocket;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Socket createSocket(String host, int port) throws IOException {
|
||||||
|
System.out.printf("Creating RMI client socket connected to %s:%d%n", host, port);
|
||||||
|
// just call the default client socket factory
|
||||||
|
return RMISocketFactory.getDefaultSocketFactory()
|
||||||
|
.createSocket(host, port);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// File server backlog value
|
||||||
|
private static final int BACKLOG_OF_5 = 5;
|
||||||
|
// Test objects factory class filename
|
||||||
|
private static final String OBJ_FACTORY_CLASS_NAME = "TestFactory.class";
|
||||||
|
// Package directory of the test's objects factory class
|
||||||
|
private static final Path OBJ_FACTORY_PACKAGE_PATH = Paths.get("com").resolve("test");
|
||||||
|
}
|
@ -0,0 +1,74 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 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.
|
||||||
|
*
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import javax.naming.ConfigurationException;
|
||||||
|
import javax.naming.NamingException;
|
||||||
|
import javax.naming.Reference;
|
||||||
|
import javax.naming.spi.ObjectFactory;
|
||||||
|
import javax.naming.spi.ObjectFactoryBuilder;
|
||||||
|
import java.lang.reflect.InvocationTargetException;
|
||||||
|
import java.net.MalformedURLException;
|
||||||
|
import java.net.URL;
|
||||||
|
import java.net.URLClassLoader;
|
||||||
|
import java.util.Hashtable;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test library class that implements {@code javax.naming.spi.ObjectFactoryBuilder} interface.
|
||||||
|
* Its implementation allows object factory class loading from any remote location.
|
||||||
|
*/
|
||||||
|
public class TestObjectFactoryBuilder implements ObjectFactoryBuilder {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ObjectFactory createObjectFactory(Object obj, Hashtable<?, ?> environment) throws NamingException {
|
||||||
|
System.err.println("TestObjectFactoryBuilder: Creating new object factory");
|
||||||
|
System.err.println("Builder for object: " + obj);
|
||||||
|
System.err.println("And for environment: " + environment);
|
||||||
|
// Only objects of the Reference type are supported, others are rejected
|
||||||
|
if (obj instanceof Reference ref) {
|
||||||
|
String objectFactoryLocation = ref.getFactoryClassLocation();
|
||||||
|
try {
|
||||||
|
URL factoryURL = new URL(objectFactoryLocation);
|
||||||
|
var cl = new URLClassLoader(new URL[]{factoryURL});
|
||||||
|
Class<?> factoryClass = cl.loadClass(ref.getFactoryClassName());
|
||||||
|
System.err.println("Loaded object factory: " + factoryClass);
|
||||||
|
if (ObjectFactory.class.isAssignableFrom(factoryClass)) {
|
||||||
|
return (ObjectFactory) factoryClass
|
||||||
|
.getDeclaredConstructor().newInstance();
|
||||||
|
} else {
|
||||||
|
throw new ConfigurationException("Test configuration error -" +
|
||||||
|
" loaded object factory of wrong type");
|
||||||
|
}
|
||||||
|
} catch (MalformedURLException e) {
|
||||||
|
throw new ConfigurationException("Error constructing test object factory");
|
||||||
|
} catch (ClassNotFoundException | NoSuchMethodException | InstantiationException |
|
||||||
|
IllegalAccessException | InvocationTargetException e) {
|
||||||
|
throw new ConfigurationException("Test configuration error: " +
|
||||||
|
"factory class cannot be loaded from the provided " +
|
||||||
|
"object factory location");
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
throw new ConfigurationException("Test factory builder " +
|
||||||
|
"supports only Reference types");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user