1
18
19 package org.xnio;
20
21 import java.io.Closeable;
22 import java.io.File;
23 import java.io.FileNotFoundException;
24 import java.io.IOException;
25 import java.lang.management.ManagementFactory;
26 import java.net.InetSocketAddress;
27 import java.nio.channels.FileChannel;
28 import java.nio.file.NoSuchFileException;
29 import java.nio.file.Path;
30 import java.nio.file.StandardOpenOption;
31 import java.security.AccessController;
32 import java.security.GeneralSecurityException;
33 import java.security.PrivilegedAction;
34 import java.util.EnumMap;
35 import java.util.EnumSet;
36 import java.util.Iterator;
37 import java.util.ServiceLoader;
38 import java.util.concurrent.atomic.AtomicBoolean;
39
40 import javax.management.MBeanServer;
41 import javax.management.ObjectName;
42
43 import org.osgi.framework.Bundle;
44 import org.osgi.framework.BundleContext;
45 import org.osgi.framework.FrameworkUtil;
46 import org.osgi.framework.ServiceReference;
47 import org.xnio.management.XnioProviderMXBean;
48 import org.xnio.management.XnioServerMXBean;
49 import org.xnio.management.XnioWorkerMXBean;
50 import org.xnio.ssl.JsseSslUtils;
51 import org.xnio.ssl.JsseXnioSsl;
52 import org.xnio.ssl.XnioSsl;
53
54 import javax.net.ssl.KeyManager;
55 import javax.net.ssl.TrustManager;
56
57 import static java.security.AccessController.doPrivileged;
58 import static org.xnio._private.Messages.msg;
59
60
65 @SuppressWarnings("unused")
66 public abstract class Xnio {
67
68 static final InetSocketAddress ANY_INET_ADDRESS = new InetSocketAddress(0);
69 static final LocalSocketAddress ANY_LOCAL_ADDRESS = new LocalSocketAddress("");
70
71 private static final EnumMap<FileAccess, OptionMap> FILE_ACCESS_OPTION_MAPS;
72
73 private static final RuntimePermission ALLOW_BLOCKING_SETTING = new RuntimePermission("changeThreadBlockingSetting");
74
75 static final class MBeanHolder {
76
77 private static final MBeanServer MBEAN_SERVER;
78
79 static {
80 MBEAN_SERVER = doPrivileged(new PrivilegedAction<MBeanServer>() {
81 public MBeanServer run() {
82 return ManagementFactory.getPlatformMBeanServer();
83 }
84 });
85 }
86 }
87
88
92 public static final boolean NIO2 = true;
93
94 static {
95 msg.greeting(Version.VERSION);
96 final EnumMap<FileAccess, OptionMap> map = new EnumMap<FileAccess, OptionMap>(FileAccess.class);
97 for (FileAccess access : FileAccess.values()) {
98 map.put(access, OptionMap.create(Options.FILE_ACCESS, access));
99 }
100 FILE_ACCESS_OPTION_MAPS = map;
101 }
102
103
106 private final String name;
107
108
114 protected Xnio(String name) {
115 if (name == null) {
116 throw msg.nullParameter("name");
117 }
118 this.name = name;
119 }
120
121 private static final ThreadLocal<Boolean> BLOCKING = new ThreadLocal<Boolean>() {
122 protected Boolean initialValue() {
123 return Boolean.TRUE;
124 }
125 };
126
127
135 public static boolean allowBlocking(boolean newSetting) throws SecurityException {
136 final SecurityManager sm = System.getSecurityManager();
137 if (sm != null) {
138 sm.checkPermission(ALLOW_BLOCKING_SETTING);
139 }
140 final ThreadLocal<Boolean> threadLocal = BLOCKING;
141 try {
142 return threadLocal.get().booleanValue();
143 } finally {
144 threadLocal.set(Boolean.valueOf(newSetting));
145 }
146 }
147
148
153 public static boolean isBlockingAllowed() {
154 return BLOCKING.get().booleanValue();
155 }
156
157
162 public static void checkBlockingAllowed() throws IllegalStateException {
163 if (! BLOCKING.get().booleanValue()) {
164 throw msg.blockingNotAllowed();
165 }
166 }
167
168
177 public static Xnio getInstance(final ClassLoader classLoader) {
178 return doGetInstance(null, doPrivileged(new PrivilegedAction<ServiceLoader<XnioProvider>>() {
179 public ServiceLoader<XnioProvider> run() {
180 return ServiceLoader.load(XnioProvider.class, classLoader);
181 }
182 }));
183 }
184
185
193 public static Xnio getInstance() {
194 return doGetInstance(null, doPrivileged(new PrivilegedAction<ServiceLoader<XnioProvider>>() {
195 public ServiceLoader<XnioProvider> run() {
196 return ServiceLoader.load(XnioProvider.class, Xnio.class.getClassLoader());
197 }
198 }));
199 }
200
201
210 public static Xnio getInstance(String provider, final ClassLoader classLoader) {
211 return doGetInstance(provider, doPrivileged(new PrivilegedAction<ServiceLoader<XnioProvider>>() {
212 public ServiceLoader<XnioProvider> run() {
213 return ServiceLoader.load(XnioProvider.class, classLoader);
214 }
215 }));
216 }
217
218
226 public static Xnio getInstance(String provider) {
227 return doGetInstance(provider, doPrivileged(new PrivilegedAction<ServiceLoader<XnioProvider>>() {
228 public ServiceLoader<XnioProvider> run() {
229 return ServiceLoader.load(XnioProvider.class, Xnio.class.getClassLoader());
230 }
231 }));
232 }
233
234 private static synchronized Xnio doGetInstance(final String provider, final ServiceLoader<XnioProvider> serviceLoader) {
235 final Iterator<XnioProvider> iterator = serviceLoader.iterator();
236 for (;;) {
237 try {
238 if (! iterator.hasNext()) break;
239 final XnioProvider xnioProvider = iterator.next();
240 try {
241 if (provider == null || provider.equals(xnioProvider.getName())) {
242 return xnioProvider.getInstance();
243 }
244 } catch (Throwable t) {
245 msg.debugf(t, "Not loading provider %s", xnioProvider.getName());
246 }
247 } catch (Throwable t) {
248 msg.debugf(t, "Skipping non-loadable provider");
249 }
250 }
251 try {
252 Xnio xnio = OsgiSupport.doGetOsgiService();
253 if (xnio != null) {
254 return xnio;
255 }
256 } catch (NoClassDefFoundError t) {
257
258 } catch (Throwable t) {
259 msg.debugf(t, "Not using OSGi service");
260 }
261 throw msg.noProviderFound();
262 }
263
264 static class OsgiSupport {
265
266 static Xnio doGetOsgiService() {
267 Bundle bundle = FrameworkUtil.getBundle(Xnio.class);
268 BundleContext context = bundle.getBundleContext();
269 if (context == null) {
270 throw new IllegalStateException("Bundle not started");
271 }
272 ServiceReference<Xnio> sr = context.getServiceReference(Xnio.class);
273 if (sr == null) {
274 return null;
275 }
276 return context.getService(sr);
277 }
278
279 }
280
281
282
283
284
285
286
287
294 public XnioSsl getSslProvider(final OptionMap optionMap) throws GeneralSecurityException {
295 return new JsseXnioSsl(this, optionMap);
296 }
297
298
307 public XnioSsl getSslProvider(final KeyManager[] keyManagers, final TrustManager[] trustManagers, final OptionMap optionMap) throws GeneralSecurityException {
308 return new JsseXnioSsl(this, optionMap, JsseSslUtils.createSSLContext(keyManagers, trustManagers, null, optionMap));
309 }
310
311
312
313
314
315
316
317
325 public FileChannel openFile(File file, OptionMap options) throws IOException {
326 if (file == null) {
327 throw msg.nullParameter("file");
328 }
329 if (options == null) {
330 throw msg.nullParameter("options");
331 }
332 try {
333 final FileAccess fileAccess = options.get(Options.FILE_ACCESS, FileAccess.READ_WRITE);
334 final boolean append = options.get(Options.FILE_APPEND, false);
335 final boolean create = options.get(Options.FILE_CREATE, fileAccess != FileAccess.READ_ONLY);
336 final EnumSet<StandardOpenOption> openOptions = EnumSet.noneOf(StandardOpenOption.class);
337 if (create) {
338 openOptions.add(StandardOpenOption.CREATE);
339 }
340 if (fileAccess.isRead()) {
341 openOptions.add(StandardOpenOption.READ);
342 }
343 if (fileAccess.isWrite()) {
344 openOptions.add(StandardOpenOption.WRITE);
345 }
346 if (append) {
347 openOptions.add(StandardOpenOption.APPEND);
348 }
349 final Path path = file.toPath();
350 return new XnioFileChannel(path.getFileSystem().provider().newFileChannel(path, openOptions));
351 } catch (NoSuchFileException e) {
352 throw new FileNotFoundException(e.getMessage());
353 }
354 }
355
356
364 public FileChannel openFile(String fileName, OptionMap options) throws IOException {
365 if (fileName == null) {
366 throw msg.nullParameter("fileName");
367 }
368 return openFile(new File(fileName), options);
369 }
370
371
379 public FileChannel openFile(File file, FileAccess access) throws IOException {
380 if (access == null) {
381 throw msg.nullParameter("access");
382 }
383 return openFile(file, FILE_ACCESS_OPTION_MAPS.get(access));
384 }
385
386
394 public FileChannel openFile(String fileName, FileAccess access) throws IOException {
395 if (access == null) {
396 throw msg.nullParameter("access");
397 }
398 if (fileName == null) {
399 throw msg.nullParameter("fileName");
400 }
401 return openFile(new File(fileName), FILE_ACCESS_OPTION_MAPS.get(access));
402 }
403
404
410 protected FileChannel unwrapFileChannel(FileChannel src) {
411 if (src instanceof XnioFileChannel) {
412 return ((XnioFileChannel)src).getDelegate();
413 } else {
414 return src;
415 }
416 }
417
418
419
420
421
422
423
424
429 public XnioWorker.Builder createWorkerBuilder() {
430 return new XnioWorker.Builder(this);
431 }
432
433
439 protected abstract XnioWorker build(XnioWorker.Builder builder);
440
441
449 public XnioWorker createWorker(OptionMap optionMap) throws IOException, IllegalArgumentException {
450 return createWorker(null, optionMap);
451 }
452
453
462 public XnioWorker createWorker(ThreadGroup threadGroup, OptionMap optionMap) throws IOException, IllegalArgumentException {
463 return createWorker(threadGroup, optionMap, null);
464 }
465
466
476 public XnioWorker createWorker(ThreadGroup threadGroup, OptionMap optionMap, Runnable terminationTask) throws IOException, IllegalArgumentException {
477 final XnioWorker.Builder workerBuilder = createWorkerBuilder();
478 workerBuilder.populateFromOptions(optionMap);
479 workerBuilder.setThreadGroup(threadGroup);
480 workerBuilder.setTerminationTask(terminationTask);
481 return workerBuilder.build();
482 }
483
484
491 public FileSystemWatcher createFileSystemWatcher(final String name, final OptionMap options) {
492 int pollInterval = options.get(Options.WATCHER_POLL_INTERVAL, 5000);
493 boolean daemonThread = options.get(Options.THREAD_DAEMON, true);
494 return new PollingFileSystemWatcher(name, pollInterval, daemonThread);
495 }
496
497
498
499
500
501
502
503
508 public final String getName() {
509 return name;
510 }
511
512
517 public final String toString() {
518 return String.format("XNIO provider \"%s\" <%s@%s>", getName(), getClass().getName(), Integer.toHexString(hashCode()));
519 }
520
521
528 protected static String getProperty(final String name) {
529 if (! name.startsWith("xnio.")) {
530 throw msg.propReadForbidden();
531 }
532 final SecurityManager sm = System.getSecurityManager();
533 if (sm != null) {
534 return AccessController.doPrivileged(new ReadPropertyAction(name, null));
535 } else {
536 return System.getProperty(name);
537 }
538 }
539
540
548 protected static String getProperty(final String name, final String defaultValue) {
549 if (! name.startsWith("xnio.")) {
550 throw msg.propReadForbidden();
551 }
552 final SecurityManager sm = System.getSecurityManager();
553 if (sm != null) {
554 return AccessController.doPrivileged(new ReadPropertyAction(name, defaultValue));
555 } else {
556 return System.getProperty(name, defaultValue);
557 }
558 }
559
560
566 protected static Closeable register(XnioProviderMXBean providerMXBean) {
567 try {
568 final ObjectName objectName = new ObjectName("org.xnio", ObjectProperties.properties(ObjectProperties.property("type", "Xnio"), ObjectProperties.property("provider", ObjectName.quote(providerMXBean.getName()))));
569 MBeanHolder.MBEAN_SERVER.registerMBean(providerMXBean, objectName);
570 return new MBeanCloseable(objectName);
571 } catch (Throwable ignored) {
572 return IoUtils.nullCloseable();
573 }
574 }
575
576
582 protected static Closeable register(XnioWorkerMXBean workerMXBean) {
583 try {
584 final ObjectName objectName = new ObjectName("org.xnio", ObjectProperties.properties(ObjectProperties.property("type", "Xnio"), ObjectProperties.property("provider", ObjectName.quote(workerMXBean.getProviderName())), ObjectProperties.property("worker", ObjectName.quote(workerMXBean.getName()))));
585 MBeanHolder.MBEAN_SERVER.registerMBean(workerMXBean, objectName);
586 return new MBeanCloseable(objectName);
587 } catch (Throwable ignored) {
588 return IoUtils.nullCloseable();
589 }
590 }
591
592
598 protected static Closeable register(XnioServerMXBean serverMXBean) {
599 try {
600 final ObjectName objectName = new ObjectName("org.xnio", ObjectProperties.properties(ObjectProperties.property("type", "Xnio"), ObjectProperties.property("provider", ObjectName.quote(serverMXBean.getProviderName())), ObjectProperties.property("worker", ObjectName.quote(serverMXBean.getWorkerName())), ObjectProperties.property("address", ObjectName.quote(serverMXBean.getBindAddress()))));
601 MBeanHolder.MBEAN_SERVER.registerMBean(serverMXBean, objectName);
602 return new MBeanCloseable(objectName);
603 } catch (Throwable ignored) {
604 return IoUtils.nullCloseable();
605 }
606 }
607
608 static class MBeanCloseable extends AtomicBoolean implements Closeable {
609
610 private final ObjectName objectName;
611
612 MBeanCloseable(final ObjectName objectName) {
613 this.objectName = objectName;
614 }
615
616 public void close() {
617 if (! getAndSet(true)) try {
618 MBeanHolder.MBEAN_SERVER.unregisterMBean(objectName);
619 } catch (Throwable ignored) {
620 }
621 }
622 }
623 }
624