8338536: Permanently disable remote code downloading in JNDI

Reviewed-by: dfuchs
This commit is contained in:
Aleksei Efimov 2024-11-21 20:55:02 +00:00
parent 7709d435d0
commit cee74f9e67
11 changed files with 364 additions and 252 deletions

View File

@ -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.
*
* This code is free software; you can redistribute it and/or modify it

View File

@ -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.
*
* This code is free software; you can redistribute it and/or modify it

View File

@ -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.
*
* This code is free software; you can redistribute it and/or modify it
@ -236,7 +236,7 @@ final class Obj {
if (!VersionHelper.isSerialDataAllowed()) {
throw new NamingException("Object deserialization is not allowed");
}
ClassLoader cl = helper.getURLClassLoader(codebases);
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)
@ -246,7 +246,7 @@ final class Obj {
// For backward compatibility only
return decodeRmiObject(
(String)attrs.get(JAVA_ATTRIBUTES[CLASSNAME]).get(),
(String)attr.get(), codebases);
(String)attr.get());
}
attr = attrs.get(JAVA_ATTRIBUTES[OBJECT_CLASS]);
@ -368,7 +368,7 @@ final class Obj {
* @deprecated For backward compatibility only
*/
private static Object decodeRmiObject(String className,
String rmiName, String[] codebases) throws NamingException {
String rmiName) throws NamingException {
return new Reference(className, new StringRefAddr("URL", rmiName));
}
@ -410,7 +410,7 @@ final class Obj {
int start, sep, posn;
Base64.Decoder decoder = null;
ClassLoader cl = helper.getURLClassLoader(codebases);
ClassLoader cl = Thread.currentThread().getContextClassLoader();
/*
* Temporary array for decoded RefAddr addresses - used to ensure

View File

@ -25,22 +25,10 @@
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 {
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
* 'javaSerializedData', 'javaRemoteLocation' or 'javaReferenceAddress' LDAP attributes.
@ -48,29 +36,13 @@ public final class VersionHelper {
private static final boolean trustSerialData;
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
// 'javaSerializedData', 'javaRemoteLocation' or 'javaReferenceAddress' attributes.
String trustSerialDataSp = getPrivilegedProperty(
String trustSerialDataSp = System.getProperty(
"com.sun.jndi.ldap.object.trustSerialData", "false");
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() {
}
@ -89,41 +61,12 @@ public final class VersionHelper {
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 {
return Class.forName(className, true, getContextClassLoader());
return Class.forName(className, true,
Thread.currentThread().getContextClassLoader());
}
Thread createThread(Runnable 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;
}
}

View File

@ -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.
*
* This code is free software; you can redistribute it and/or modify it
@ -181,9 +181,8 @@ public class NamingManagerHelper {
static ObjectFactory getObjectFactoryFromReference(
Reference ref, String factoryName, Predicate<Class<?>> filter)
throws IllegalAccessException,
InstantiationException,
MalformedURLException {
Class<?> clas = null;
InstantiationException {
Class<?> clas;
// Try to use current class loader
try {
@ -193,27 +192,11 @@ public class NamingManagerHelper {
return null;
}
} catch (ClassNotFoundException e) {
// ignore and continue
// e.printStackTrace();
}
// All other exceptions are passed up.
// 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) {
}
}
assert clas != null;
@SuppressWarnings("deprecation") // Class.newInstance
ObjectFactory result = (clas != null) ? (ObjectFactory) clas.newInstance() : null;
ObjectFactory result = (ObjectFactory) clas.newInstance();
return result;
}
@ -401,12 +384,6 @@ public class NamingManagerHelper {
ObjectFactoryBuilder builder) throws NamingException {
if (object_factory_builder != null)
throw new IllegalStateException("ObjectFactoryBuilder already set");
@SuppressWarnings("removal")
SecurityManager security = System.getSecurityManager();
if (security != null) {
security.checkSetFactory();
}
object_factory_builder = builder;
}

View File

@ -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.
*
* 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 java.io.IOException;
import java.io.InputStream;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLClassLoader;
import java.nio.file.Files;
import java.nio.file.Path;
import java.security.AccessController;
import java.security.PrivilegedAction;
import java.security.PrivilegedActionException;
import java.security.PrivilegedExceptionAction;
import java.util.*;
/**
@ -53,21 +47,6 @@ import java.util.*;
public final class 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[]{
javax.naming.Context.INITIAL_CONTEXT_FACTORY,
javax.naming.Context.OBJECT_FACTORIES,
@ -101,22 +80,6 @@ public final class VersionHelper {
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.
* <p>
@ -136,37 +99,19 @@ public final class VersionHelper {
/*
* Returns a JNDI property from the system properties. Returns
* null if the property is not set, or if there is no permission
* to read it.
* null if the property is not set.
*/
@SuppressWarnings("removal")
String getJndiProperty(int i) {
PrivilegedAction<String> act = () -> {
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
* returns their values -- in order -- in an array. For each
* unset property, the corresponding array element is set to null.
* Returns null if there is no permission to call System.getProperties().
*/
String[] getJndiProperties() {
PrivilegedAction<Properties> act = () -> {
try {
return System.getProperties();
} catch (SecurityException e) {
return null;
}
};
@SuppressWarnings("removal")
Properties sysProps = AccessController.doPrivileged(act);
Properties sysProps = System.getProperties();
if (sysProps == null) {
return null;
}
@ -199,16 +144,12 @@ public final class VersionHelper {
* Returns the resource of a given name associated with a particular
* class (never null), or null if none can be found.
*/
@SuppressWarnings("removal")
InputStream getResourceAsStream(Class<?> c, String name) {
PrivilegedAction<InputStream> act = () -> {
try {
return c.getModule().getResourceAsStream(resolveName(c, name));
} catch (IOException x) {
return null;
}
};
return AccessController.doPrivileged(act);
}
/*
@ -217,9 +158,7 @@ public final class VersionHelper {
*
* @param filename The file name, sans directory.
*/
@SuppressWarnings("removal")
InputStream getJavaHomeConfStream(String filename) {
PrivilegedAction<InputStream> act = () -> {
try {
String javahome = System.getProperty("java.home");
if (javahome == null) {
@ -229,8 +168,6 @@ public final class VersionHelper {
} catch (Exception e) {
return null;
}
};
return AccessController.doPrivileged(act);
}
/*
@ -239,19 +176,12 @@ public final class VersionHelper {
* loader. Null represents the bootstrap class loader in some
* Java implementations.
*/
@SuppressWarnings("removal")
NamingEnumeration<InputStream> getResources(ClassLoader cl,
String name) throws IOException {
Enumeration<URL> urls;
PrivilegedExceptionAction<Enumeration<URL>> act = () ->
(cl == null)
urls = (cl == null)
? ClassLoader.getSystemResources(name)
: cl.getResources(name);
try {
urls = AccessController.doPrivileged(act);
} catch (PrivilegedActionException e) {
throw (IOException) e.getException();
}
return new InputStreamEnumeration(urls);
}
@ -265,39 +195,18 @@ public final class VersionHelper {
* Please don't expose this method as public.
* @throws SecurityException if the class loader is not accessible
*/
@SuppressWarnings("removal")
ClassLoader getContextClassLoader() {
PrivilegedAction<ClassLoader> act = () -> {
ClassLoader loader = Thread.currentThread().getContextClassLoader();
if (loader == null) {
// 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]);
}
/**
* Given an enumeration of URLs, an instance of this class represents
* an enumeration of their InputStreams. Each operation on the URL
* enumeration is performed within a doPrivileged block.
* This is used to enumerate the resources under a foreign codebase.
* This class is not MT-safe.
* an enumeration of their InputStreams.
*/
private class InputStreamEnumeration implements
NamingEnumeration<InputStream> {
@ -314,9 +223,7 @@ public final class VersionHelper {
* Returns the next InputStream, or null if there are no more.
* An InputStream that cannot be opened is skipped.
*/
@SuppressWarnings("removal")
private InputStream getNextElement() {
PrivilegedAction<InputStream> act = () -> {
while (urls.hasMoreElements()) {
try {
return urls.nextElement().openStream();
@ -325,8 +232,6 @@ public final class VersionHelper {
}
}
return null;
};
return AccessController.doPrivileged(act);
}
public boolean hasMore() {

View File

@ -123,9 +123,12 @@ public class NamingManager {
* or {@code Referenceable} containing a factory class name,
* use the named factory to create the object.
* Return {@code refInfo} if the factory cannot be created.
* Under JDK 1.1, if the factory class must be loaded from a location
* specified in the reference, a {@code SecurityManager} must have
* been installed or the factory creation will fail.
* Downloading a factory class from a location specified in the reference
* can be supported by a custom implementation of {@link ObjectFactoryBuilder}.
* 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,
* it is passed up to the caller.
* <li>If {@code refInfo} is a {@code Reference} or

View File

@ -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.
*
* 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.registry.Registry;
import java.rmi.registry.LocateRegistry;
import java.security.AccessController;
import java.security.PrivilegedAction;
import javax.naming.*;
import javax.naming.spi.NamingManager;
@ -57,19 +55,6 @@ public class RegistryContext implements Context, Referenceable {
private int port;
private static final NameParser nameParser = new AtomicNameParser();
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
@ -481,12 +466,6 @@ public class RegistryContext implements Context, Referenceable {
? ((RemoteReference)r).getReference()
: (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
Reference ref = null;
if (obj instanceof Reference) {
@ -495,11 +474,14 @@ public class RegistryContext implements Context, Referenceable {
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(
"The object factory is untrusted. Set the system property" +
" 'com.sun.jndi.rmi.object.trustURLCodebase' to 'true'.");
"Remote object factories are not supported");
}
return NamingManagerHelper.getObjectInstance(obj, name, this,
environment, ObjectFactoriesFilter::checkRmiFilter);

View File

@ -58,6 +58,11 @@
* implementation.
* </li>
* </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
* @moduleGraph
* @since 9

View File

@ -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");
}

View File

@ -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");
}
}
}