View Javadoc
1   /*
2    * junixsocket
3    *
4    * Copyright 2009-2024 Christian Kohlschütter
5    *
6    * Licensed under the Apache License, Version 2.0 (the "License");
7    * you may not use this file except in compliance with the License.
8    * You may obtain a copy of the License at
9    *
10   *     http://www.apache.org/licenses/LICENSE-2.0
11   *
12   * Unless required by applicable law or agreed to in writing, software
13   * distributed under the License is distributed on an "AS IS" BASIS,
14   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15   * See the License for the specific language governing permissions and
16   * limitations under the License.
17   */
18  package org.newsclub.net.unix.rmi;
19  
20  import java.io.Closeable;
21  import java.io.IOException;
22  import java.lang.ref.WeakReference;
23  import java.rmi.NoSuchObjectException;
24  import java.rmi.RemoteException;
25  import java.rmi.ServerException;
26  import java.util.ArrayList;
27  import java.util.BitSet;
28  import java.util.Iterator;
29  import java.util.List;
30  import java.util.Objects;
31  import java.util.Random;
32  import java.util.concurrent.ExecutorService;
33  import java.util.concurrent.Executors;
34  import java.util.concurrent.ThreadLocalRandom;
35  import java.util.stream.IntStream;
36  
37  import org.newsclub.net.unix.StackTraceUtil;
38  
39  import com.kohlschutter.annotations.compiletime.SuppressFBWarnings;
40  
41  /**
42   * A very simple implementation of a {@link AFRMIService}.
43   *
44   * @author Christian Kohlschütter
45   */
46  final class AFRMIServiceImpl implements AFRMIService {
47    private final BitSet ports = new BitSet(1000);
48    private final WeakReference<AFNaming> naming;
49    private final List<WeakReference<Closeable>> closeAtShutdown = new ArrayList<>();
50  
51    public AFRMIServiceImpl(AFNaming naming) {
52      this.naming = new WeakReference<>(naming);
53    }
54  
55    @SuppressFBWarnings("DMI_RANDOM_USED_ONLY_ONCE")
56    private/* synchronized */int randomPort() {
57      int maxRandom = ports.size();
58  
59      Random random = ThreadLocalRandom.current();
60  
61      int port;
62      for (int i = 0; i < 3; i++) {
63        port = ports.nextClearBit(random.nextInt(maxRandom));
64        if (port < maxRandom) {
65          return port;
66        } else {
67          maxRandom = port;
68          if (maxRandom == 0) {
69            break;
70          }
71        }
72      }
73      return ports.nextClearBit(0);
74    }
75  
76    @Override
77    public synchronized int newPort() throws IOException {
78      int port = randomPort();
79      ports.set(port);
80      port += RMIPorts.ANONYMOUS_PORT_BASE;
81      return port;
82    }
83  
84    @Override
85    public synchronized void returnPort(int port) throws IOException {
86      ports.clear(port - RMIPorts.ANONYMOUS_PORT_BASE);
87    }
88  
89    @Override
90    public IntStream openPorts() throws RemoteException {
91      return ports.stream().map((int v) -> {
92        return v + RMIPorts.ANONYMOUS_PORT_BASE;
93      });
94    }
95  
96    @Override
97    public void shutdown() throws RemoteException {
98      AFNaming namingInstance = naming.get();
99      if (namingInstance != null) {
100       if (!namingInstance.isRemoteShutdownAllowed()) {
101         throw new ServerException("Remote shutdown is disabled");
102       }
103       try {
104         namingInstance.shutdownRegistry();
105       } catch (ShutdownException e) {
106         // already shut down
107       }
108     }
109   }
110 
111   @Override
112   public boolean isShutdownAllowed() throws RemoteException {
113     AFNaming namingInstance = naming.get();
114     if (namingInstance != null) {
115       return namingInstance.isRemoteShutdownAllowed();
116     } else {
117       return true;
118     }
119   }
120 
121   @Override
122   public void registerForShutdown(Closeable closeable) throws RemoteException {
123     synchronized (closeAtShutdown) {
124       unregisterForShutdown(closeable);
125       closeAtShutdown.add(new WeakReference<>(closeable));
126     }
127   }
128 
129   @Override
130   public void unregisterForShutdown(Closeable closeable) throws RemoteException {
131     synchronized (closeAtShutdown) {
132       Objects.requireNonNull(closeable);
133       for (Iterator<WeakReference<Closeable>> it = closeAtShutdown.iterator(); it.hasNext();) {
134         if (closeable.equals(it.next().get())) {
135           it.remove();
136           return;
137         }
138       }
139     }
140   }
141 
142   void shutdownRegisteredCloseables() {
143     List<WeakReference<Closeable>> list;
144     synchronized (closeAtShutdown) {
145       list = new ArrayList<>(closeAtShutdown);
146       closeAtShutdown.clear();
147     }
148 
149     ExecutorService executor = Executors.newCachedThreadPool();
150     for (WeakReference<Closeable> ref : list) {
151       executor.execute(() -> {
152         @SuppressWarnings("resource")
153         Closeable cl = ref.get();
154         if (cl == null) {
155           return;
156         }
157         try {
158           cl.close();
159         } catch (NoSuchObjectException e) {
160           // ignore
161         } catch (IOException e) {
162           StackTraceUtil.printStackTrace(e);
163         }
164       });
165     }
166     executor.shutdown();
167   }
168 }