AFRMIServiceImpl.java
/*
* junixsocket
*
* Copyright 2009-2024 Christian Kohlschütter
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.newsclub.net.unix.rmi;
import java.io.Closeable;
import java.io.IOException;
import java.lang.ref.WeakReference;
import java.rmi.NoSuchObjectException;
import java.rmi.RemoteException;
import java.rmi.ServerException;
import java.util.ArrayList;
import java.util.BitSet;
import java.util.Iterator;
import java.util.List;
import java.util.Objects;
import java.util.Random;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadLocalRandom;
import java.util.stream.IntStream;
import org.newsclub.net.unix.StackTraceUtil;
import com.kohlschutter.annotations.compiletime.SuppressFBWarnings;
/**
* A very simple implementation of a {@link AFRMIService}.
*
* @author Christian Kohlschütter
*/
final class AFRMIServiceImpl implements AFRMIService {
private final BitSet ports = new BitSet(1000);
private final WeakReference<AFNaming> naming;
private final List<WeakReference<Closeable>> closeAtShutdown = new ArrayList<>();
public AFRMIServiceImpl(AFNaming naming) {
this.naming = new WeakReference<>(naming);
}
@SuppressFBWarnings("DMI_RANDOM_USED_ONLY_ONCE")
private/* synchronized */int randomPort() {
int maxRandom = ports.size();
Random random = ThreadLocalRandom.current();
int port;
for (int i = 0; i < 3; i++) {
port = ports.nextClearBit(random.nextInt(maxRandom));
if (port < maxRandom) {
return port;
} else {
maxRandom = port;
if (maxRandom == 0) {
break;
}
}
}
return ports.nextClearBit(0);
}
@Override
public synchronized int newPort() throws IOException {
int port = randomPort();
ports.set(port);
port += RMIPorts.ANONYMOUS_PORT_BASE;
return port;
}
@Override
public synchronized void returnPort(int port) throws IOException {
ports.clear(port - RMIPorts.ANONYMOUS_PORT_BASE);
}
@Override
public IntStream openPorts() throws RemoteException {
return ports.stream().map((int v) -> {
return v + RMIPorts.ANONYMOUS_PORT_BASE;
});
}
@Override
public void shutdown() throws RemoteException {
AFNaming namingInstance = naming.get();
if (namingInstance != null) {
if (!namingInstance.isRemoteShutdownAllowed()) {
throw new ServerException("Remote shutdown is disabled");
}
try {
namingInstance.shutdownRegistry();
} catch (ShutdownException e) {
// already shut down
}
}
}
@Override
public boolean isShutdownAllowed() throws RemoteException {
AFNaming namingInstance = naming.get();
if (namingInstance != null) {
return namingInstance.isRemoteShutdownAllowed();
} else {
return true;
}
}
@Override
public void registerForShutdown(Closeable closeable) throws RemoteException {
synchronized (closeAtShutdown) {
unregisterForShutdown(closeable);
closeAtShutdown.add(new WeakReference<>(closeable));
}
}
@Override
public void unregisterForShutdown(Closeable closeable) throws RemoteException {
synchronized (closeAtShutdown) {
Objects.requireNonNull(closeable);
for (Iterator<WeakReference<Closeable>> it = closeAtShutdown.iterator(); it.hasNext();) {
if (closeable.equals(it.next().get())) {
it.remove();
return;
}
}
}
}
void shutdownRegisteredCloseables() {
List<WeakReference<Closeable>> list;
synchronized (closeAtShutdown) {
list = new ArrayList<>(closeAtShutdown);
closeAtShutdown.clear();
}
ExecutorService executor = Executors.newCachedThreadPool();
for (WeakReference<Closeable> ref : list) {
executor.execute(() -> {
@SuppressWarnings("resource")
Closeable cl = ref.get();
if (cl == null) {
return;
}
try {
cl.close();
} catch (NoSuchObjectException e) {
// ignore
} catch (IOException e) {
StackTraceUtil.printStackTrace(e);
}
});
}
executor.shutdown();
}
}