1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18 package org.newsclub.net.unix.rmi;
19
20 import java.io.File;
21 import java.io.IOException;
22 import java.io.ObjectInput;
23 import java.io.ObjectOutput;
24 import java.rmi.server.RMIClientSocketFactory;
25 import java.rmi.server.RMIServerSocketFactory;
26 import java.rmi.server.RMISocketFactory;
27 import java.util.HashMap;
28 import java.util.Map;
29 import java.util.Objects;
30
31 import org.newsclub.net.unix.AFSocket;
32 import org.newsclub.net.unix.AFSocketAddress;
33 import org.newsclub.net.unix.AFUNIXSocket;
34 import org.newsclub.net.unix.AFUNIXSocketAddress;
35 import org.newsclub.net.unix.AFUNIXSocketCredentials;
36 import org.newsclub.net.unix.HostAndPort;
37
38 import com.kohlschutter.annotations.compiletime.SuppressFBWarnings;
39
40
41
42
43
44
45 public class AFUNIXRMISocketFactory extends AFRMISocketFactory {
46 private static final long serialVersionUID = 1L;
47
48 static final String DEFAULT_SOCKET_FILE_PREFIX = "";
49 static final String DEFAULT_SOCKET_FILE_SUFFIX = ".rmi";
50
51 private File socketDir;
52 private String socketPrefix;
53 private String socketSuffix;
54
55 private final transient Map<HostAndPort, AFUNIXSocketCredentials> credentials = new HashMap<>();
56
57
58
59
60
61
62 public AFUNIXRMISocketFactory() {
63 super();
64 }
65
66
67
68
69
70
71
72
73
74
75
76
77
78 @SuppressFBWarnings("EI_EXPOSE_REP2")
79 public AFUNIXRMISocketFactory(final AFNaming naming, final File socketDir,
80 final RMIClientSocketFactory defaultClientFactory,
81 final RMIServerSocketFactory defaultServerFactory, final String socketPrefix,
82 final String socketSuffix) {
83 super(naming, defaultClientFactory, defaultServerFactory);
84 Objects.requireNonNull(socketDir);
85 this.socketDir = socketDir;
86 this.socketPrefix = socketPrefix == null ? DEFAULT_SOCKET_FILE_PREFIX : socketPrefix;
87 this.socketSuffix = socketSuffix == null ? DEFAULT_SOCKET_FILE_SUFFIX : socketSuffix;
88 }
89
90
91
92
93
94
95
96
97
98 public AFUNIXRMISocketFactory(AFNaming naming, File socketDir,
99 RMIClientSocketFactory defaultClientFactory, RMIServerSocketFactory defaultServerFactory) {
100 this(naming, socketDir, defaultClientFactory, defaultServerFactory, null, null);
101 }
102
103
104
105
106
107
108
109 public AFUNIXRMISocketFactory(AFNaming naming, File socketDir) {
110 this(naming, socketDir, DefaultRMIClientSocketFactory.getInstance(),
111 DefaultRMIServerSocketFactory.getInstance());
112 }
113
114 @Override
115 protected AFNaming readNamingInstance(ObjectInput in) throws IOException {
116 socketDir = new File(in.readUTF());
117 int port = in.readInt();
118 return AFUNIXNaming.getInstance(socketDir, port);
119 }
120
121 @Override
122 protected void writeNamingInstance(ObjectOutput out, AFNaming naming) throws IOException {
123 out.writeUTF(socketDir.getAbsolutePath());
124 out.writeInt(naming.getRegistryPort());
125 }
126
127 @Override
128 public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
129 super.readExternal(in);
130
131 socketPrefix = in.readUTF();
132 socketSuffix = in.readUTF();
133 }
134
135 @Override
136 public void writeExternal(ObjectOutput out) throws IOException {
137 super.writeExternal(out);
138
139 out.writeUTF(socketPrefix);
140 out.writeUTF(socketSuffix);
141 }
142
143 @Override
144 public int hashCode() {
145 return socketDir == null ? System.identityHashCode(this) : socketDir.hashCode();
146 }
147
148 @Override
149 public boolean equals(Object other) {
150 if (!(other instanceof AFUNIXRMISocketFactory)) {
151 return false;
152 }
153 AFUNIXRMISocketFactory sf = (AFUNIXRMISocketFactory) other;
154 if (socketDir == null) {
155 return sf == this;
156 } else {
157 return socketDir.equals(sf.socketDir);
158 }
159 }
160
161
162
163
164
165
166 public File getSocketDir() {
167 return socketDir;
168 }
169
170 File getFile(int port) {
171 if (isPlainFileSocket()) {
172 return getSocketDir();
173 } else {
174 Objects.requireNonNull(socketDir);
175 return new File(socketDir, socketPrefix + port + socketSuffix);
176 }
177 }
178
179 boolean hasSocketFile(int port) {
180 return getFile(port).exists();
181 }
182
183 private boolean isPlainFileSocket() {
184 return (getNaming().getRegistryPort() == RMIPorts.PLAIN_FILE_SOCKET);
185 }
186
187 @Override
188 protected AFUNIXSocketAddress newSocketAddress(int port) throws IOException {
189 return AFUNIXSocketAddress.of(getFile(port), port);
190 }
191
192 @Override
193 protected final AFSocket<?> newConnectedSocket(AFSocketAddress addr) throws IOException {
194 final AFUNIXSocket socket = ((AFUNIXSocketAddress) addr).newConnectedSocket();
195 AFUNIXSocketCredentials creds = socket.getPeerCredentials();
196
197 final HostAndPort hap = new HostAndPort(addr.getHostString(), addr.getPort());
198 synchronized (credentials) {
199 if (credentials.put(hap, creds) != null) {
200
201 }
202 }
203 socket.addCloseable(() -> {
204 synchronized (credentials) {
205 credentials.remove(hap);
206 }
207 });
208 return socket;
209 }
210
211 @Override
212 public String toString() {
213 return super.toString() +
214 "[path=" + socketDir +
215 (isPlainFileSocket() ? "" :
216 ";prefix=" + socketPrefix + ";suffix=" + socketSuffix) + "]";
217 }
218
219 @Override
220 public void close() throws IOException {
221 synchronized (credentials) {
222 credentials.clear();
223 }
224 super.close();
225 }
226
227 AFUNIXSocketCredentials peerCredentialsFor(RemotePeerInfo data) {
228 synchronized (credentials) {
229 return credentials.get(new HostAndPort(data.host, data.port));
230 }
231 }
232
233 @Override
234 boolean hasRegisteredPort(int port) {
235 return hasSocketFile(port);
236 }
237 }