/** * @author <a href="http://tfox.org">Tim Fox</a> * @author Juergen Donnerstag */ public class WindowsFileSystem extends FileSystemImpl { private static final Logger log = LoggerFactory.getLogger(WindowsFileSystem.class); public WindowsFileSystem(final VertxInternal vertx) { super(vertx); } private static void logInternal(final String perms) { if (perms != null && log.isDebugEnabled()) { log.debug("You are running on Windows and POSIX style file permissions are not supported"); } } @Override protected BlockingAction<Void> chmodInternal( String path, String perms, String dirPerms, Handler<AsyncResult<Void>> handler) { logInternal(perms); logInternal(dirPerms); return new BlockingAction<Void>(vertx, handler) { @Override public Void action() { return null; } }; } @Override protected BlockingAction<Void> mkdirInternal( String path, final String perms, final boolean createParents, Handler<AsyncResult<Void>> handler) { logInternal(perms); return super.mkdirInternal(path, null, createParents, handler); } @Override protected AsyncFile doOpen( String path, String perms, boolean read, boolean write, boolean createNew, boolean flush, ContextImpl context) { logInternal(perms); return new AsyncFileImpl(vertx, path, null, read, write, createNew, flush, context); } @Override protected BlockingAction<Void> createFileInternal( String p, final String perms, Handler<AsyncResult<Void>> handler) { logInternal(perms); return super.createFileInternal(p, null, handler); } @Override protected BlockingAction<Void> chownInternal( String path, String user, String group, Handler<AsyncResult<Void>> handler) { if (group != null && log.isDebugEnabled()) { log.debug("You are running on Windows and POSIX style file ownership is not supported"); } return super.chownInternal(path, user, group, handler); } }
/** @author <a href="http://tfox.org">Tim Fox</a> */ public class DeploymentManager { private static final Logger log = LoggerFactory.getLogger(DeploymentManager.class); private final VertxInternal vertx; private final Map<String, Deployment> deployments = new ConcurrentHashMap<>(); private final Map<String, ClassLoader> classloaders = new WeakHashMap<>(); private Map<String, VerticleFactory> verticleFactories = new ConcurrentHashMap<>(); private static final VerticleFactory DEFAULT_VERTICLE_FACTORY = new SimpleJavaVerticleFactory(); public DeploymentManager(VertxInternal vertx) { this.vertx = vertx; loadVerticleFactories(); } private void loadVerticleFactories() { ServiceLoader<VerticleFactory> factories = ServiceLoader.load(VerticleFactory.class); Iterator<VerticleFactory> iter = factories.iterator(); while (iter.hasNext()) { VerticleFactory factory = iter.next(); factory.init(vertx); String prefix = factory.prefix(); if (verticleFactories.containsKey(prefix)) { log.warn( "Not loading verticle factory: " + factory + " as prefix " + prefix + " is already in use"); } else { verticleFactories.put(prefix, factory); } } } public void deployVerticle( Verticle verticle, DeploymentOptions options, Handler<AsyncResult<String>> completionHandler) { ContextImpl currentContext = vertx.getOrCreateContext(); doDeploy(verticle, options, currentContext, completionHandler); } public void deployVerticle( String verticleName, DeploymentOptions options, Handler<AsyncResult<String>> completionHandler) { ContextImpl currentContext = vertx.getOrCreateContext(); ClassLoader cl = getClassLoader(options.getIsolationGroup()); int pos = verticleName.indexOf(':'); if (pos == -1) { throw new IllegalArgumentException("verticleName must start with prefix"); } String prefix = verticleName.substring(0, pos); if (pos + 1 >= verticleName.length()) { throw new IllegalArgumentException("Invalid name: " + verticleName); } String actualName = verticleName.substring(pos + 1); VerticleFactory verticleFactory = verticleFactories.get(prefix); if (verticleFactory == null) { // Use default Java verticle factory verticleFactory = DEFAULT_VERTICLE_FACTORY; } try { Verticle verticle = verticleFactory.createVerticle(actualName, cl); if (verticle == null) { reportFailure( new NullPointerException("VerticleFactory::createVerticle returned null"), currentContext, completionHandler); } else { doDeploy(verticle, options, currentContext, completionHandler); } } catch (Exception e) { reportFailure(e, currentContext, completionHandler); } } public void undeployVerticle(String deploymentID, Handler<AsyncResult<Void>> completionHandler) { Deployment deployment = deployments.get(deploymentID); Context currentContext = vertx.getOrCreateContext(); if (deployment == null) { reportFailure( new IllegalStateException("Unknown deployment"), currentContext, completionHandler); } else { deployment.undeploy(completionHandler); } } public Set<String> deployments() { return Collections.unmodifiableSet(deployments.keySet()); } public void undeployAll(Handler<AsyncResult<Void>> completionHandler) { Set<String> deploymentIDs = new HashSet<>(deployments.keySet()); AtomicInteger count = new AtomicInteger(deploymentIDs.size()); for (String deploymentID : deploymentIDs) { undeployVerticle( deploymentID, ar -> { if (ar.failed()) { log.error("Undeploy failed", ar.cause()); } if (count.incrementAndGet() == deploymentIDs.size()) { completionHandler.handle(new FutureResultImpl<>((Void) null)); } }); } } public void registerVerticleFactory(VerticleFactory factory) { if (factory.prefix() == null) { throw new IllegalArgumentException("factory.prefix() cannot be null"); } if (verticleFactories.containsKey(factory.prefix())) { throw new IllegalArgumentException( "There is already a registered verticle factory with prefix " + factory.prefix()); } verticleFactories.put(factory.prefix(), factory); } public void unregisterVerticleFactory(VerticleFactory factory) { if (verticleFactories.remove(factory.prefix()) == null) { throw new IllegalArgumentException("Factory " + factory + " is not registered"); } } public Set<VerticleFactory> verticleFactories() { return new HashSet<>(verticleFactories.values()); } private ClassLoader getClassLoader(String isolationGroup) { ClassLoader cl; if (isolationGroup == null) { cl = getCurrentClassLoader(); } else { synchronized (this) { cl = classloaders.get(isolationGroup); if (cl == null) { ClassLoader current = getCurrentClassLoader(); if (!(current instanceof URLClassLoader)) { throw new IllegalStateException("Current classloader must be URLClassLoader"); } URLClassLoader urlc = (URLClassLoader) current; URL[] urls = urlc.getURLs(); // Copy the URLS into the isolating classloader cl = new IsolatingClassLoader(urls, getCurrentClassLoader()); classloaders.put(isolationGroup, cl); } } } return cl; } private ClassLoader getCurrentClassLoader() { ClassLoader cl = Thread.currentThread().getContextClassLoader(); if (cl == null) { cl = getClass().getClassLoader(); } return cl; } private <T> void reportFailure( Throwable t, Context context, Handler<AsyncResult<T>> completionHandler) { if (completionHandler != null) { reportResult(context, completionHandler, new FutureResultImpl<>(t)); } else { log.error(t.getMessage(), t); } } private <T> void reportSuccess( T result, Context context, Handler<AsyncResult<T>> completionHandler) { if (completionHandler != null) { reportResult(context, completionHandler, new FutureResultImpl<>(result)); } } private <T> void reportResult( Context context, Handler<AsyncResult<T>> completionHandler, AsyncResult<T> result) { context.runOnContext( v -> { try { completionHandler.handle(result); } catch (Throwable t) { log.error("Failure in calling handler", t); } }); } private void doDeploy( Verticle verticle, DeploymentOptions options, ContextImpl currentContext, Handler<AsyncResult<String>> completionHandler) { if (options.isMultiThreaded() && !options.isWorker()) { throw new IllegalArgumentException("If multi-threaded then must be worker too"); } ContextImpl context = options.isWorker() ? vertx.createWorkerContext(options.isMultiThreaded()) : vertx.createEventLoopContext(); String deploymentID = UUID.randomUUID().toString(); DeploymentImpl deployment = new DeploymentImpl(deploymentID, context, verticle); context.setDeployment(deployment); Deployment parent = currentContext.getDeployment(); if (parent != null) { parent.addChild(deployment); } JsonObject conf = options.getConfig() == null ? null : options.getConfig().copy(); // Copy it context.runOnContext( v -> { try { verticle.setVertx(vertx); verticle.setConfig(conf); verticle.setDeploymentID(deploymentID); Future<Void> startFuture = new FutureResultImpl<>(); verticle.start(startFuture); startFuture.setHandler( ar -> { if (ar.succeeded()) { deployments.put(deploymentID, deployment); reportSuccess(deploymentID, currentContext, completionHandler); } else { reportFailure(ar.cause(), currentContext, completionHandler); } }); } catch (Throwable t) { reportFailure(t, currentContext, completionHandler); } }); } private class DeploymentImpl implements Deployment { private final String id; private final ContextImpl context; private final Verticle verticle; private final Set<Deployment> children = new ConcurrentHashSet<>(); private boolean undeployed; private DeploymentImpl(String id, ContextImpl context, Verticle verticle) { this.id = id; this.context = context; this.verticle = verticle; } @Override public void undeploy(Handler<AsyncResult<Void>> completionHandler) { ContextImpl currentContext = vertx.getOrCreateContext(); if (!undeployed) { doUndeploy(currentContext, completionHandler); } else { reportFailure( new IllegalStateException("Already undeployed"), currentContext, completionHandler); } } public void doUndeploy( ContextImpl undeployingContext, Handler<AsyncResult<Void>> completionHandler) { if (!children.isEmpty()) { final int size = children.size(); AtomicInteger childCount = new AtomicInteger(); for (Deployment childDeployment : new HashSet<>(children)) { childDeployment.doUndeploy( undeployingContext, ar -> { children.remove(childDeployment); if (ar.failed()) { reportFailure(ar.cause(), undeployingContext, completionHandler); } else if (childCount.incrementAndGet() == size) { // All children undeployed doUndeploy(undeployingContext, completionHandler); } }); } } else { undeployed = true; context.runOnContext( v -> { Future<Void> stopFuture = new FutureResultImpl<>(); stopFuture.setHandler( ar -> { deployments.remove(id); context.runCloseHooks( ar2 -> { if (ar2.failed()) { // Log error but we report success anyway log.error("Failed to run close hook", ar2.cause()); } if (ar.succeeded()) { reportSuccess(null, undeployingContext, completionHandler); } else { reportFailure(ar.cause(), undeployingContext, completionHandler); } }); }); try { verticle.stop(stopFuture); } catch (Throwable t) { stopFuture.setFailure(t); } }); } } @Override public void addChild(Deployment deployment) { children.add(deployment); } } /** * Before delegating to the parent, this classloader attempts to load the class first (opposite of * normal delegation model). This allows multiple versions of the same class to be loaded by * different classloaders which allows us to isolate verticles so they can't easily interact */ private static class IsolatingClassLoader extends URLClassLoader { private IsolatingClassLoader(URL[] urls, ClassLoader parent) { super(urls, parent); } @Override protected Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException { synchronized (getClassLoadingLock(name)) { Class<?> c = findLoadedClass(name); if (c == null) { // We don't want to load Vert.x (or Vert.x dependency) classes from an isolating loader if (isVertxOrSystemClass(name)) { try { c = super.loadClass(name, false); } catch (ClassNotFoundException e) { // Fall through } } if (c == null) { // Try and load with this classloader try { c = findClass(name); } catch (ClassNotFoundException e) { // Now try with parent c = super.loadClass(name, false); } } } if (resolve) { resolveClass(c); } return c; } } private boolean isVertxOrSystemClass(String name) { return name.startsWith("java.") || name.startsWith("javax.") || name.startsWith("com.sun.") || name.startsWith("io.vertx.core") || name.startsWith("com.hazelcast") || name.startsWith("io.netty."); } } }
/** @author <a href="http://tfox.org">Tim Fox</a> */ class HazelcastAsyncMultiMap<K, V> implements AsyncMultiMap<K, V>, EntryListener<K, V> { private static final Logger log = LoggerFactory.getLogger(HazelcastAsyncMultiMap.class); private final VertxSPI vertx; private final com.hazelcast.core.MultiMap<K, V> map; /* The Hazelcast near cache is very slow so we use our own one. Keeping it in sync is a little tricky. As entries are added or removed the EntryListener will be called but when the node joins the cluster it isn't provided the initial state via the EntryListener Therefore the first time get is called for a subscription we *always* get the subs from Hazelcast (this is what the initialised flag is for), then consider that the initial state. While the get is in progress the entry listener may be being called, so we merge any pre-existing entries so we don't lose any. Hazelcast doesn't seem to have any consistent way to get an initial state plus a stream of updates. */ private ConcurrentMap<K, ChoosableSet<V>> cache = new ConcurrentHashMap<>(); public HazelcastAsyncMultiMap(VertxSPI vertx, com.hazelcast.core.MultiMap<K, V> map) { this.vertx = vertx; this.map = map; map.addEntryListener(this, true); } @Override public void removeAllForValue(final V val, final Handler<AsyncResult<Void>> completionHandler) { vertx.executeBlocking( () -> { for (Map.Entry<K, V> entry : map.entrySet()) { V v = entry.getValue(); if (val.equals(v)) { map.remove(entry.getKey(), v); } } return null; }, completionHandler); } @Override public void add(final K k, final V v, final Handler<AsyncResult<Void>> completionHandler) { vertx.executeBlocking( () -> { map.put(k, HazelcastServerID.convertServerID(v)); return null; }, completionHandler); } @Override public void get(final K k, final Handler<AsyncResult<ChoosableIterable<V>>> resultHandler) { ChoosableSet<V> entries = cache.get(k); Future<ChoosableIterable<V>> result = Future.future(); if (entries != null && entries.isInitialised()) { result.setResult(entries).setHandler(resultHandler); } else { vertx.executeBlocking( () -> map.get(k), (AsyncResult<Collection<V>> res2) -> { Future<ChoosableIterable<V>> sresult = Future.future(); if (res2.succeeded()) { Collection<V> entries2 = res2.result(); ChoosableSet<V> sids; if (entries2 != null) { sids = new ChoosableSet<>(entries2.size()); for (V hid : entries2) { sids.add(hid); } } else { sids = new ChoosableSet<>(0); } ChoosableSet<V> prev = cache.putIfAbsent(k, sids); if (prev != null) { // Merge them prev.merge(sids); sids = prev; } sids.setInitialised(); sresult.setResult(sids); } else { sresult.setFailure(result.cause()); } sresult.setHandler(resultHandler); }); } } @Override public void remove(final K k, final V v, final Handler<AsyncResult<Boolean>> completionHandler) { vertx.executeBlocking(() -> map.remove(k, v), completionHandler); } @Override public void entryAdded(EntryEvent<K, V> entry) { addEntry(entry.getKey(), entry.getValue()); } private void addEntry(K k, V v) { ChoosableSet<V> entries = cache.get(k); if (entries == null) { entries = new ChoosableSet<>(1); ChoosableSet<V> prev = cache.putIfAbsent(k, entries); if (prev != null) { entries = prev; } } entries.add(v); } @Override public void entryRemoved(EntryEvent<K, V> entry) { removeEntry(entry.getKey(), entry.getValue()); } private void removeEntry(K k, V v) { ChoosableSet<V> entries = cache.get(k); if (entries != null) { entries.remove(v); if (entries.isEmpty()) { cache.remove(k); } } } @Override public void entryUpdated(EntryEvent<K, V> entry) { K k = entry.getKey(); ChoosableSet<V> entries = cache.get(k); if (entries != null) { entries.add(entry.getValue()); } } @Override public void entryEvicted(EntryEvent<K, V> entry) { entryRemoved(entry); } }
/** @author <a href="http://tfox.org">Tim Fox</a> */ public class FileSystemImpl implements FileSystem { @SuppressWarnings("unused") private static final Logger log = LoggerFactory.getLogger(FileSystemImpl.class); protected final VertxInternal vertx; public FileSystemImpl(VertxInternal vertx) { this.vertx = vertx; } public FileSystem copy(String from, String to, Handler<AsyncResult<Void>> handler) { copyInternal(from, to, handler).run(); return this; } public FileSystem copySync(String from, String to) { copyInternal(from, to, null).perform(); return this; } public FileSystem copyRecursive( String from, String to, boolean recursive, Handler<AsyncResult<Void>> handler) { copyInternal(from, to, recursive, handler).run(); return this; } public FileSystem copyRecursiveSync(String from, String to, boolean recursive) { copyInternal(from, to, recursive, null).perform(); return this; } public FileSystem move(String from, String to, Handler<AsyncResult<Void>> handler) { moveInternal(from, to, handler).run(); return this; } public FileSystem moveSync(String from, String to) { moveInternal(from, to, null).perform(); return this; } public FileSystem truncate(String path, long len, Handler<AsyncResult<Void>> handler) { truncateInternal(path, len, handler).run(); return this; } public FileSystem truncateSync(String path, long len) { truncateInternal(path, len, null).perform(); return this; } public FileSystem chmod(String path, String perms, Handler<AsyncResult<Void>> handler) { chmodInternal(path, perms, handler).run(); return this; } public FileSystem chmodSync(String path, String perms) { chmodInternal(path, perms, null).perform(); return this; } public FileSystem chmodRecursive( String path, String perms, String dirPerms, Handler<AsyncResult<Void>> handler) { chmodInternal(path, perms, dirPerms, handler).run(); return this; } public FileSystem chmodRecursiveSync(String path, String perms, String dirPerms) { chmodInternal(path, perms, dirPerms, null).perform(); return this; } public FileSystem chown( String path, String user, String group, Handler<AsyncResult<Void>> handler) { chownInternal(path, user, group, handler).run(); return this; } public FileSystem chownSync(String path, String user, String group) { chownInternal(path, user, group, null).perform(); return this; } public FileSystem props(String path, Handler<AsyncResult<FileProps>> handler) { propsInternal(path, handler).run(); return this; } public FileProps propsSync(String path) { return propsInternal(path, null).perform(); } public FileSystem lprops(String path, Handler<AsyncResult<FileProps>> handler) { lpropsInternal(path, handler).run(); return this; } public FileProps lpropsSync(String path) { return lpropsInternal(path, null).perform(); } public FileSystem link(String link, String existing, Handler<AsyncResult<Void>> handler) { linkInternal(link, existing, handler).run(); return this; } public FileSystem linkSync(String link, String existing) { linkInternal(link, existing, null).perform(); return this; } public FileSystem symlink(String link, String existing, Handler<AsyncResult<Void>> handler) { symlinkInternal(link, existing, handler).run(); return this; } public FileSystem symlinkSync(String link, String existing) { symlinkInternal(link, existing, null).perform(); return this; } public FileSystem unlink(String link, Handler<AsyncResult<Void>> handler) { unlinkInternal(link, handler).run(); return this; } public FileSystem unlinkSync(String link) { unlinkInternal(link, null).perform(); return this; } public FileSystem readSymlink(String link, Handler<AsyncResult<String>> handler) { readSymlinkInternal(link, handler).run(); return this; } public String readSymlinkSync(String link) { return readSymlinkInternal(link, null).perform(); } public FileSystem delete(String path, Handler<AsyncResult<Void>> handler) { deleteInternal(path, handler).run(); return this; } public FileSystem deleteSync(String path) { deleteInternal(path, null).perform(); return this; } public FileSystem deleteRecursive( String path, boolean recursive, Handler<AsyncResult<Void>> handler) { deleteInternal(path, recursive, handler).run(); return this; } public FileSystem deleteSyncRecursive(String path, boolean recursive) { deleteInternal(path, recursive, null).perform(); return this; } public FileSystem mkdir(String path, Handler<AsyncResult<Void>> handler) { mkdirInternal(path, handler).run(); return this; } public FileSystem mkdirSync(String path) { mkdirInternal(path, null).perform(); return this; } public FileSystem mkdirs(String path, Handler<AsyncResult<Void>> handler) { mkdirInternal(path, true, handler).run(); return this; } public FileSystem mkdirsSync(String path) { mkdirInternal(path, true, null).perform(); return this; } public FileSystem mkdir(String path, String perms, Handler<AsyncResult<Void>> handler) { mkdirInternal(path, perms, handler).run(); return this; } public FileSystem mkdirSync(String path, String perms) { mkdirInternal(path, perms, null).perform(); return this; } public FileSystem mkdirs(String path, String perms, Handler<AsyncResult<Void>> handler) { mkdirInternal(path, perms, true, handler).run(); return this; } public FileSystem mkdirsSync(String path, String perms) { mkdirInternal(path, perms, true, null).perform(); return this; } public FileSystem readDir(String path, Handler<AsyncResult<List<String>>> handler) { readDirInternal(path, handler).run(); return this; } public List<String> readDirSync(String path) { return readDirInternal(path, null).perform(); } public FileSystem readDir( String path, String filter, Handler<AsyncResult<List<String>>> handler) { readDirInternal(path, filter, handler).run(); return this; } public List<String> readDirSync(String path, String filter) { return readDirInternal(path, filter, null).perform(); } public FileSystem readFile(String path, Handler<AsyncResult<Buffer>> handler) { readFileInternal(path, handler).run(); return this; } public Buffer readFileSync(String path) { return readFileInternal(path, null).perform(); } public FileSystem writeFile(String path, Buffer data, Handler<AsyncResult<Void>> handler) { writeFileInternal(path, data, handler).run(); return this; } public FileSystem writeFileSync(String path, Buffer data) { writeFileInternal(path, data, null).perform(); return this; } public FileSystem open( String path, OpenOptions options, Handler<AsyncResult<AsyncFile>> handler) { openInternal(path, options, handler).run(); return this; } public AsyncFile openSync(String path, OpenOptions options) { return openInternal(path, options, null).perform(); } public FileSystem createFile(String path, Handler<AsyncResult<Void>> handler) { createFileInternal(path, handler).run(); return this; } public FileSystem createFileSync(String path) { createFileInternal(path, null).perform(); return this; } public FileSystem createFile(String path, String perms, Handler<AsyncResult<Void>> handler) { createFileInternal(path, perms, handler).run(); return this; } public FileSystem createFileSync(String path, String perms) { createFileInternal(path, perms, null).perform(); return this; } public FileSystem exists(String path, Handler<AsyncResult<Boolean>> handler) { existsInternal(path, handler).run(); return this; } public boolean existsSync(String path) { return existsInternal(path, null).perform(); } public FileSystem fsProps(String path, Handler<AsyncResult<FileSystemProps>> handler) { fsPropsInternal(path, handler).run(); return this; } public FileSystemProps fsPropsSync(String path) { return fsPropsInternal(path, null).perform(); } private BlockingAction<Void> copyInternal( String from, String to, Handler<AsyncResult<Void>> handler) { return copyInternal(from, to, false, handler); } private BlockingAction<Void> copyInternal( String from, String to, final boolean recursive, Handler<AsyncResult<Void>> handler) { final Path source = PathAdjuster.adjust(vertx, Paths.get(from)); final Path target = PathAdjuster.adjust(vertx, Paths.get(to)); return new BlockingAction<Void>(handler) { public Void perform() { try { if (recursive) { Files.walkFileTree( source, EnumSet.of(FileVisitOption.FOLLOW_LINKS), Integer.MAX_VALUE, new SimpleFileVisitor<Path>() { public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) throws IOException { Path targetDir = target.resolve(source.relativize(dir)); try { Files.copy(dir, targetDir); } catch (FileAlreadyExistsException e) { if (!Files.isDirectory(targetDir)) { throw e; } } return FileVisitResult.CONTINUE; } @Override public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException { Files.copy(file, target.resolve(source.relativize(file))); return FileVisitResult.CONTINUE; } }); } else { Files.copy(source, target); } } catch (IOException e) { throw new FileSystemException(e); } return null; } }; } private BlockingAction<Void> moveInternal( String from, String to, Handler<AsyncResult<Void>> handler) { final Path source = PathAdjuster.adjust(vertx, Paths.get(from)); final Path target = PathAdjuster.adjust(vertx, Paths.get(to)); return new BlockingAction<Void>(handler) { public Void perform() { try { Files.move(source, target); } catch (IOException e) { throw new FileSystemException(e); } return null; } }; } private BlockingAction<Void> truncateInternal( String p, final long len, Handler<AsyncResult<Void>> handler) { final String path = PathAdjuster.adjust(vertx, p); return new BlockingAction<Void>(handler) { public Void perform() { if (len < 0) { throw new FileSystemException("Cannot truncate file to size < 0"); } if (!Files.exists(Paths.get(path))) { throw new FileSystemException("Cannot truncate file " + path + ". Does not exist"); } RandomAccessFile raf = null; try { try { raf = new RandomAccessFile(path, "rw"); raf.setLength(len); } finally { if (raf != null) raf.close(); } } catch (IOException e) { throw new FileSystemException(e); } return null; } }; } private BlockingAction<Void> chmodInternal( String path, String perms, Handler<AsyncResult<Void>> handler) { return chmodInternal(path, perms, null, handler); } protected BlockingAction<Void> chmodInternal( String path, String perms, String dirPerms, Handler<AsyncResult<Void>> handler) { final Path target = PathAdjuster.adjust(vertx, Paths.get(path)); final Set<PosixFilePermission> permissions = PosixFilePermissions.fromString(perms); final Set<PosixFilePermission> dirPermissions = dirPerms == null ? null : PosixFilePermissions.fromString(dirPerms); return new BlockingAction<Void>(handler) { public Void perform() { try { if (dirPermissions != null) { Files.walkFileTree( target, new SimpleFileVisitor<Path>() { public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) throws IOException { // The directory entries typically have different permissions to the files, e.g. // execute permission // or can't cd into it Files.setPosixFilePermissions(dir, dirPermissions); return FileVisitResult.CONTINUE; } public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException { Files.setPosixFilePermissions(file, permissions); return FileVisitResult.CONTINUE; } }); } else { Files.setPosixFilePermissions(target, permissions); } } catch (SecurityException e) { throw new FileSystemException("Accessed denied for chmod on " + target); } catch (IOException e) { throw new FileSystemException(e); } return null; } }; } protected BlockingAction<Void> chownInternal( String path, final String user, final String group, Handler<AsyncResult<Void>> handler) { final Path target = PathAdjuster.adjust(vertx, Paths.get(path)); final UserPrincipalLookupService service = target.getFileSystem().getUserPrincipalLookupService(); return new BlockingAction<Void>(handler) { public Void perform() { try { final UserPrincipal userPrincipal = user == null ? null : service.lookupPrincipalByName(user); final GroupPrincipal groupPrincipal = group == null ? null : service.lookupPrincipalByGroupName(group); if (groupPrincipal != null) { PosixFileAttributeView view = Files.getFileAttributeView( target, PosixFileAttributeView.class, LinkOption.NOFOLLOW_LINKS); if (view == null) { throw new FileSystemException("Change group of file not supported"); } view.setGroup(groupPrincipal); } if (userPrincipal != null) { Files.setOwner(target, userPrincipal); } } catch (SecurityException e) { throw new FileSystemException("Accessed denied for chown on " + target); } catch (IOException e) { throw new FileSystemException(e); } return null; } }; } private BlockingAction<FileProps> propsInternal( String path, Handler<AsyncResult<FileProps>> handler) { return props(path, true, handler); } private BlockingAction<FileProps> lpropsInternal( String path, Handler<AsyncResult<FileProps>> handler) { return props(path, false, handler); } private BlockingAction<FileProps> props( String path, final boolean followLinks, Handler<AsyncResult<FileProps>> handler) { final Path target = PathAdjuster.adjust(vertx, Paths.get(path)); return new BlockingAction<FileProps>(handler) { public FileProps perform() { try { BasicFileAttributes attrs; if (followLinks) { attrs = Files.readAttributes(target, BasicFileAttributes.class); } else { attrs = Files.readAttributes(target, BasicFileAttributes.class, LinkOption.NOFOLLOW_LINKS); } return new FilePropsImpl(attrs); } catch (IOException e) { throw new FileSystemException(e); } } }; } private BlockingAction<Void> linkInternal( String link, String existing, Handler<AsyncResult<Void>> handler) { return link(link, existing, false, handler); } private BlockingAction<Void> symlinkInternal( String link, String existing, Handler<AsyncResult<Void>> handler) { return link(link, existing, true, handler); } private BlockingAction<Void> link( String link, String existing, final boolean symbolic, Handler<AsyncResult<Void>> handler) { final Path source = PathAdjuster.adjust(vertx, Paths.get(link)); final Path target = PathAdjuster.adjust(vertx, Paths.get(existing)); return new BlockingAction<Void>(handler) { public Void perform() { try { if (symbolic) { Files.createSymbolicLink(source, target); } else { Files.createLink(source, target); } } catch (IOException e) { throw new FileSystemException(e); } return null; } }; } private BlockingAction<Void> unlinkInternal(String link, Handler<AsyncResult<Void>> handler) { return deleteInternal(link, handler); } private BlockingAction<String> readSymlinkInternal( String link, Handler<AsyncResult<String>> handler) { final Path source = PathAdjuster.adjust(vertx, Paths.get(link)); return new BlockingAction<String>(handler) { public String perform() { try { return Files.readSymbolicLink(source).toString(); } catch (IOException e) { throw new FileSystemException(e); } } }; } private BlockingAction<Void> deleteInternal(String path, Handler<AsyncResult<Void>> handler) { return deleteInternal(path, false, handler); } private BlockingAction<Void> deleteInternal( String path, final boolean recursive, Handler<AsyncResult<Void>> handler) { final Path source = PathAdjuster.adjust(vertx, Paths.get(path)); return new BlockingAction<Void>(handler) { public Void perform() { try { if (recursive) { Files.walkFileTree( source, new SimpleFileVisitor<Path>() { public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException { Files.delete(file); return FileVisitResult.CONTINUE; } public FileVisitResult postVisitDirectory(Path dir, IOException e) throws IOException { if (e == null) { Files.delete(dir); return FileVisitResult.CONTINUE; } else { throw e; } } }); } else { Files.delete(source); } } catch (IOException e) { throw new FileSystemException(e); } return null; } }; } private BlockingAction<Void> mkdirInternal(String path, Handler<AsyncResult<Void>> handler) { return mkdirInternal(path, null, false, handler); } private BlockingAction<Void> mkdirInternal( String path, boolean createParents, Handler<AsyncResult<Void>> handler) { return mkdirInternal(path, null, createParents, handler); } private BlockingAction<Void> mkdirInternal( String path, String perms, Handler<AsyncResult<Void>> handler) { return mkdirInternal(path, perms, false, handler); } protected BlockingAction<Void> mkdirInternal( String path, final String perms, final boolean createParents, Handler<AsyncResult<Void>> handler) { final Path source = PathAdjuster.adjust(vertx, Paths.get(path)); final FileAttribute<?> attrs = perms == null ? null : PosixFilePermissions.asFileAttribute(PosixFilePermissions.fromString(perms)); return new BlockingAction<Void>(handler) { public Void perform() { try { if (createParents) { if (attrs != null) { Files.createDirectories(source, attrs); } else { Files.createDirectories(source); } } else { if (attrs != null) { Files.createDirectory(source, attrs); } else { Files.createDirectory(source); } } } catch (IOException e) { throw new FileSystemException(e); } return null; } }; } private BlockingAction<List<String>> readDirInternal( String path, Handler<AsyncResult<List<String>>> handler) { return readDirInternal(path, null, handler); } private BlockingAction<List<String>> readDirInternal( String p, final String filter, Handler<AsyncResult<List<String>>> handler) { final String path = PathAdjuster.adjust(vertx, p); return new BlockingAction<List<String>>(handler) { public List<String> perform() { try { File file = new File(path); if (!file.exists()) { throw new FileSystemException("Cannot read directory " + path + ". Does not exist"); } if (!file.isDirectory()) { throw new FileSystemException( "Cannot read directory " + path + ". It's not a directory"); } else { FilenameFilter fnFilter; if (filter != null) { fnFilter = new FilenameFilter() { public boolean accept(File dir, String name) { return Pattern.matches(filter, name); } }; } else { fnFilter = null; } File[] files; if (fnFilter == null) { files = file.listFiles(); } else { files = file.listFiles(fnFilter); } List<String> ret = new ArrayList<>(files.length); for (File f : files) { ret.add(f.getCanonicalPath()); } return ret; } } catch (IOException e) { throw new FileSystemException(e); } } }; } private BlockingAction<Buffer> readFileInternal( String path, Handler<AsyncResult<Buffer>> handler) { final Path target = PathAdjuster.adjust(vertx, Paths.get(path)); return new BlockingAction<Buffer>(handler) { public Buffer perform() { try { byte[] bytes = Files.readAllBytes(target); Buffer buff = Buffer.buffer(bytes); return buff; } catch (IOException e) { throw new FileSystemException(e); } } }; } private BlockingAction<Void> writeFileInternal( String path, final Buffer data, Handler<AsyncResult<Void>> handler) { Objects.requireNonNull(data, "no null data accepted"); final Path target = PathAdjuster.adjust(vertx, Paths.get(path)); return new BlockingAction<Void>(handler) { public Void perform() { try { Files.write(target, data.getBytes()); return null; } catch (IOException e) { throw new FileSystemException(e); } } }; } private BlockingAction<AsyncFile> openInternal( String p, OpenOptions options, Handler<AsyncResult<AsyncFile>> handler) { Objects.requireNonNull(options, "no null options accepted"); final String path = PathAdjuster.adjust(vertx, p); return new BlockingAction<AsyncFile>(handler) { public AsyncFile perform() { return doOpen(path, options, context); } }; } protected AsyncFile doOpen(String path, OpenOptions options, ContextImpl context) { return new AsyncFileImpl(vertx, path, options, context); } private BlockingAction<Void> createFileInternal(String path, Handler<AsyncResult<Void>> handler) { return createFileInternal(path, null, handler); } protected BlockingAction<Void> createFileInternal( String p, final String perms, Handler<AsyncResult<Void>> handler) { final String path = PathAdjuster.adjust(vertx, p); final FileAttribute<?> attrs = perms == null ? null : PosixFilePermissions.asFileAttribute(PosixFilePermissions.fromString(perms)); return new BlockingAction<Void>(handler) { public Void perform() { try { Path target = Paths.get(path); if (attrs != null) { Files.createFile(target, attrs); } else { Files.createFile(target); } } catch (IOException e) { throw new FileSystemException(e); } return null; } }; } private BlockingAction<Boolean> existsInternal( String path, Handler<AsyncResult<Boolean>> handler) { final File file = new File(PathAdjuster.adjust(vertx, path)); return new BlockingAction<Boolean>(handler) { public Boolean perform() { return file.exists(); } }; } private BlockingAction<FileSystemProps> fsPropsInternal( String path, Handler<AsyncResult<FileSystemProps>> handler) { final Path target = PathAdjuster.adjust(vertx, Paths.get(path)); return new BlockingAction<FileSystemProps>(handler) { public FileSystemProps perform() { try { FileStore fs = Files.getFileStore(target); return new FileSystemPropsImpl( fs.getTotalSpace(), fs.getUnallocatedSpace(), fs.getUsableSpace()); } catch (IOException e) { throw new FileSystemException(e); } } }; } protected abstract class BlockingAction<T> implements Action<T> { private final Handler<AsyncResult<T>> handler; protected final ContextImpl context; public BlockingAction(Handler<AsyncResult<T>> handler) { this.handler = handler; this.context = vertx.getOrCreateContext(); } /** Run the blocking action using a thread from the worker pool. */ public void run() { context.executeBlocking(this, handler); } } }