List of usage examples for com.google.common.util.concurrent ListenableFuture addListener
void addListener(Runnable listener, Executor executor);
From source file:org.waveprotocol.box.server.waveserver.WaveletContainerImpl.java
/** * Constructs an empty WaveletContainer for a wavelet. * WaveletData is not set until a delta has been applied. * * @param notifiee the subscriber to notify of wavelet updates and commits. * @param waveletState the wavelet's delta history and current state. * @param waveDomain the wave server domain. * @param storageContinuationExecutor the executor used to perform post wavelet loading logic. *///from w ww . j a va 2 s. c o m public WaveletContainerImpl(WaveletName waveletName, WaveletNotificationSubscriber notifiee, final ListenableFuture<? extends WaveletState> waveletStateFuture, String waveDomain, Executor storageContinuationExecutor) { this.waveletName = waveletName; this.notifiee = notifiee; this.sharedDomainParticipantId = waveDomain != null ? ParticipantIdUtil.makeUnsafeSharedDomainParticipantId(waveDomain) : null; this.storageContinuationExecutor = storageContinuationExecutor; ReentrantReadWriteLock readWriteLock = new ReentrantReadWriteLock(); this.readLock = readWriteLock.readLock(); this.writeLock = readWriteLock.writeLock(); waveletStateFuture.addListener(new Runnable() { @Override public void run() { acquireWriteLock(); try { Preconditions.checkState(waveletState == null, "Repeat attempts to set wavelet state"); Preconditions.checkState(state == State.LOADING, "Unexpected state %s", state); waveletState = FutureUtil.getResultOrPropagateException(waveletStateFuture, PersistenceException.class); Preconditions.checkState(waveletState.getWaveletName().equals(getWaveletName()), "Wrong wavelet state, named %s, expected %s", waveletState.getWaveletName(), getWaveletName()); state = State.OK; } catch (PersistenceException e) { LOG.warning("Failed to load wavelet " + getWaveletName(), e); state = State.CORRUPTED; } catch (InterruptedException e) { Thread.currentThread().interrupt(); LOG.warning("Interrupted loading wavelet " + getWaveletName(), e); state = State.CORRUPTED; } catch (RuntimeException e) { // TODO(soren): would be better to terminate the process in this case LOG.severe("Unexpected exception loading wavelet " + getWaveletName(), e); state = State.CORRUPTED; } finally { releaseWriteLock(); } loadLatch.countDown(); } }, storageContinuationExecutor); }
From source file:me.lucko.luckperms.commands.migration.subcommands.MigrationPowerfulPerms.java
private CommandResult run(LuckPermsPlugin plugin, List<String> args) { final Logger log = plugin.getLog(); if (!plugin.isPluginLoaded("PowerfulPerms")) { log.severe("PowerfulPerms Migration: Error -> PowerfulPerms is not loaded."); return CommandResult.STATE_ERROR; }/* ww w . j a v a 2 s.co m*/ final String address = args.get(0); final String database = args.get(1); final String username = args.get(2); final String password = args.get(3); final String dbTable = args.get(4); // Find a list of UUIDs log.info("PowerfulPerms Migration: Getting a list of UUIDs to migrate."); @Cleanup HikariDataSource hikari = new HikariDataSource(); hikari.setMaximumPoolSize(2); hikari.setDataSourceClassName("com.mysql.jdbc.jdbc2.optional.MysqlDataSource"); hikari.addDataSourceProperty("serverName", address.split(":")[0]); hikari.addDataSourceProperty("port", address.split(":")[1]); hikari.addDataSourceProperty("databaseName", database); hikari.addDataSourceProperty("user", username); hikari.addDataSourceProperty("password", password); Set<UUID> uuids = new HashSet<>(); try { @Cleanup Connection connection = hikari.getConnection(); DatabaseMetaData meta = connection.getMetaData(); @Cleanup ResultSet tables = meta.getTables(null, null, dbTable, null); if (!tables.next()) { log.severe("PowerfulPerms Migration: Error - Couldn't find table."); return CommandResult.FAILURE; } else { @Cleanup PreparedStatement columnPs = connection.prepareStatement( "SELECT COLUMN_NAME, COLUMN_TYPE FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME=?"); columnPs.setString(1, dbTable); @Cleanup ResultSet columnRs = columnPs.executeQuery(); log.info("Found table: " + dbTable); while (columnRs.next()) { log.info("" + columnRs.getString("COLUMN_NAME") + " - " + columnRs.getString("COLUMN_TYPE")); } @Cleanup PreparedStatement preparedStatement = connection.prepareStatement("SELECT `uuid` FROM " + dbTable); @Cleanup ResultSet resultSet = preparedStatement.executeQuery(); while (resultSet.next()) { uuids.add(UUID.fromString(resultSet.getString("uuid"))); } } } catch (Exception e) { e.printStackTrace(); return CommandResult.FAILURE; } if (uuids.isEmpty()) { log.severe("PowerfulPerms Migration: Error - Unable to find any UUIDs to migrate."); return CommandResult.FAILURE; } log.info("PowerfulPerms Migration: Found " + uuids.size() + " uuids. Starting migration."); PowerfulPermsPlugin ppPlugin = (PowerfulPermsPlugin) plugin.getPlugin("PowerfulPerms"); PermissionManager pm = ppPlugin.getPermissionManager(); // Groups first. log.info("PowerfulPerms Migration: Starting group migration."); Map<Integer, Group> groups = pm.getGroups(); // All versions for (Group g : groups.values()) { plugin.getDatastore().createAndLoadGroup(g.getName().toLowerCase()); final me.lucko.luckperms.groups.Group group = plugin.getGroupManager().get(g.getName().toLowerCase()); try { LogEntry.build().actor(Constants.getConsoleUUID()).actorName(Constants.getConsoleName()) .acted(group).action("create").build().submit(plugin); } catch (Exception ex) { ex.printStackTrace(); } for (Permission p : g.getOwnPermissions()) { // All versions applyPerm(group, p, plugin); } for (Group parent : g.getParents()) { // All versions try { group.setPermission("group." + parent.getName().toLowerCase(), true); LogEntry.build().actor(Constants.getConsoleUUID()).actorName(Constants.getConsoleName()) .acted(group).action("setinherit " + parent.getName().toLowerCase()) // All versions .build().submit(plugin); } catch (Exception ex) { if (!(ex instanceof ObjectAlreadyHasException)) { ex.printStackTrace(); } } } plugin.getDatastore().saveGroup(group); } log.info("PowerfulPerms Migration: Group migration complete."); // Now users. log.info("PowerfulPerms Migration: Starting user migration."); final Map<UUID, CountDownLatch> progress = new HashMap<>(); // Migrate all users and their groups for (UUID uuid : uuids) { progress.put(uuid, new CountDownLatch(2)); // Create a LuckPerms user for the UUID plugin.getDatastore().loadUser(uuid, "null"); User user = plugin.getUserManager().get(uuid); // Get a list of Permissions held by the user from the PP API. getPlayerPermissions(pm, uuid, perms -> { // Changes each version perms.forEach(p -> applyPerm(user, p, plugin)); // Update the progress so the user can be saved and unloaded. synchronized (progress) { progress.get(uuid).countDown(); if (progress.get(uuid).getCount() == 0) { plugin.getDatastore().saveUser(user); plugin.getUserManager().cleanup(user); } } }); // Migrate the user's groups to LuckPerms from PP. Callback<Map<String, List<CachedGroup>>> callback = groups1 -> { for (Map.Entry<String, List<CachedGroup>> e : groups1.entrySet()) { final String server; if (e.getKey() != null && (e.getKey().equals("") || e.getKey().equalsIgnoreCase("all"))) { server = null; } else { server = e.getKey(); } if (superLegacy) { e.getValue().stream().filter(cg -> !cg.isNegated()).map(cg -> { try { return (Group) getGroupMethod.invoke(cg); } catch (IllegalAccessException | InvocationTargetException e1) { e1.printStackTrace(); return null; } }).forEach(g -> { if (g != null) { if (server == null) { try { user.setPermission("group." + g.getName().toLowerCase(), true); LogEntry.build().actor(Constants.getConsoleUUID()) .actorName(Constants.getConsoleName()).acted(user) .action("addgroup " + g.getName().toLowerCase()).build() .submit(plugin); } catch (Exception ex) { if (!(ex instanceof ObjectAlreadyHasException)) { ex.printStackTrace(); } } } else { try { user.setPermission("group." + g.getName().toLowerCase(), true, server); LogEntry.build().actor(Constants.getConsoleUUID()) .actorName(Constants.getConsoleName()).acted(user) .action("addgroup " + g.getName().toLowerCase() + " " + server) .build().submit(plugin); } catch (Exception ex) { if (!(ex instanceof ObjectAlreadyHasException)) { ex.printStackTrace(); } } } } }); } else { e.getValue().stream().filter(g -> !g.hasExpired() && !g.isNegated()).forEach(g -> { final Group group = pm.getGroup(g.getGroupId()); if (g.willExpire()) { if (server == null) { try { user.setPermission("group." + group.getName().toLowerCase(), true, g.getExpirationDate().getTime() / 1000L); LogEntry.build().actor(Constants.getConsoleUUID()) .actorName(Constants.getConsoleName()).acted(user) .action("addtempgroup " + group.getName().toLowerCase() + " " + g.getExpirationDate().getTime() / 1000L) .build().submit(plugin); } catch (Exception ex) { if (!(ex instanceof ObjectAlreadyHasException)) { ex.printStackTrace(); } } } else { try { user.setPermission("group." + group.getName().toLowerCase(), true, server, g.getExpirationDate().getTime() / 1000L); LogEntry.build().actor(Constants.getConsoleUUID()) .actorName(Constants.getConsoleName()).acted(user) .action("addtempgroup " + group.getName().toLowerCase() + " " + g.getExpirationDate().getTime() / 1000L + " " + server) .build().submit(plugin); } catch (Exception ex) { if (!(ex instanceof ObjectAlreadyHasException)) { ex.printStackTrace(); } } } } else { if (server == null) { try { user.setPermission("group." + group.getName().toLowerCase(), true); LogEntry.build().actor(Constants.getConsoleUUID()) .actorName(Constants.getConsoleName()).acted(user) .action("addgroup " + group.getName().toLowerCase()).build() .submit(plugin); } catch (Exception ex) { if (!(ex instanceof ObjectAlreadyHasException)) { ex.printStackTrace(); } } } else { try { user.setPermission("group." + group.getName().toLowerCase(), true, server); LogEntry.build().actor(Constants.getConsoleUUID()) .actorName(Constants.getConsoleName()).acted(user) .action("addgroup " + group.getName().toLowerCase() + " " + server) .build().submit(plugin); } catch (Exception ex) { if (!(ex instanceof ObjectAlreadyHasException)) { ex.printStackTrace(); } } } } }); } } // Update the progress so the user can be saved and unloaded. synchronized (progress) { progress.get(uuid).countDown(); if (progress.get(uuid).getCount() == 0) { plugin.getDatastore().saveUser(user); plugin.getUserManager().cleanup(user); } } }; if (!legacy) { try { ListenableFuture<LinkedHashMap<String, List<CachedGroup>>> future = (ListenableFuture<LinkedHashMap<String, List<CachedGroup>>>) getPlayerGroupsMethod .invoke(pm, uuid); try { if (future.isDone()) { callback.onComplete(future.get()); } else { future.addListener(() -> { try { callback.onComplete(future.get()); } catch (InterruptedException | ExecutionException e) { e.printStackTrace(); } }, Runnable::run); } } catch (InterruptedException | ExecutionException e) { e.printStackTrace(); } } catch (IllegalAccessException | InvocationTargetException e) { log.info("PowerfulPerms Migration: Error"); e.printStackTrace(); } } else { try { getPlayerGroupsMethod.invoke(pm, uuid, new LPResultRunnable<LinkedHashMap<String, List<CachedGroup>>>() { @Override public void run() { callback.onComplete(getResult()); } }); } catch (IllegalAccessException | InvocationTargetException e) { log.info("PowerfulPerms Migration: Error"); e.printStackTrace(); } } } // All groups are migrated, but there may still be some users being migrated. // This block will wait for all users to be completed. log.info("PowerfulPerms Migration: Waiting for user migration to complete. This may take some time"); boolean sleep = true; while (sleep) { sleep = false; for (Map.Entry<UUID, CountDownLatch> e : progress.entrySet()) { if (e.getValue().getCount() != 0) { sleep = true; break; } } if (sleep) { try { Thread.sleep(5000); } catch (InterruptedException e) { e.printStackTrace(); } } } // We done. log.info("PowerfulPerms Migration: Success! Completed without any errors."); return CommandResult.SUCCESS; }
From source file:org.eclipse.che.plugin.internal.installer.PluginInstallerImpl.java
/** * Ask to install/uninstall step//from ww w.j ava 2 s.com * @param pluginInstallerCallback an optional callback used to notify if install is success or failed * @return ID of the current install */ @Override public IPluginInstall requireNewInstall(FutureCallback pluginInstallerCallback) throws PluginInstallerException { if (currentExecution.get() != null) { throw new PluginInstallerException( "There is already install in progress. Wait that this install is finished"); } Long id = idGenerator.getAndIncrement(); PluginInstallImpl pluginInstall = new PluginInstallImpl(id); final ListenableFuture<Integer> job = this.executor.submit(() -> { // run ext script ProcessBuilder pb = new ProcessBuilder( pluginConfiguration.getInstallScript().toAbsolutePath().toString()); pb.directory(pluginConfiguration.getCheHome().toFile()); pb = pb.redirectErrorStream(true); Process p = pb.start(); // collect stream InputStream is = p.getInputStream(); InputStreamReader isr = new InputStreamReader(is, Charset.defaultCharset()); BufferedReader br = new BufferedReader(isr); String line; String fullLog = ""; pluginInstall.setLog(fullLog); while ((line = br.readLine()) != null) { fullLog += (line + "\n"); pluginInstall.setLog(fullLog); } return p.waitFor(); }); pluginInstall.setFuture(job); currentExecution.set(job); if (pluginInstallerCallback != null) { Futures.addCallback(job, pluginInstallerCallback); } Futures.addCallback(job, new IntegerFutureCallback(pluginInstall, pluginInstallerCallback)); job.addListener(() -> { resetCurrentExecution(); }, this.executor); this.executions.put(id, pluginInstall); // wait a little so empty script callbacks could be setup try { Thread.sleep(100L); } catch (InterruptedException e) { throw new PluginInstallerException("Unable to get install ID", e); } return pluginInstall; }
From source file:net.floodlightcontroller.core.internal.OFSwitch.java
/** * Append a listener to receive an OFStatsReply and update the * internal OFSwitch data structures.// ww w . j a v a 2 s .c om * * This presently taps into the following stats request * messages to listen for the corresponding reply: * -- OFTableFeaturesStatsRequest * * Extend this to tap into and update other OFStatsType messages. * * @param future * @param request * @return */ private <REPLY extends OFStatsReply> ListenableFuture<List<REPLY>> addInternalStatsReplyListener( final ListenableFuture<List<REPLY>> future, OFStatsRequest<REPLY> request) { switch (request.getStatsType()) { case TABLE_FEATURES: /* case YOUR_CASE_HERE */ future.addListener(new Runnable() { /* * We know the reply will be a list of OFStatsReply. */ @SuppressWarnings("unchecked") @Override public void run() { /* * The OFConnection handles REPLY_MORE for us in the case there * are multiple OFStatsReply messages with the same XID. */ try { List<? extends OFStatsReply> replies = future.get(); if (!replies.isEmpty()) { /* * By checking only the 0th element, we assume all others are the same type. * TODO If not, what then? */ switch (replies.get(0).getStatsType()) { case TABLE_FEATURES: processOFTableFeatures((List<OFTableFeaturesStatsReply>) future.get()); break; /* case YOUR_CASE_HERE */ default: throw new Exception("Received an invalid OFStatsReply of " + replies.get(0).getStatsType().toString() + ". Expected TABLE_FEATURES."); } } } catch (Exception e) { e.printStackTrace(); } } }, MoreExecutors.sameThreadExecutor()); /* No need for another thread. */ default: break; } return future; /* either unmodified or with an additional listener */ }
From source file:bio.gcat.batch.Batch.java
public ListenableFuture<Result> execute(Collection<Tuple> tuples) { final Result result = new Result(tuples); Queue<Action> queue = new LinkedList<>(actions); if (queue.isEmpty()) return new DefiniteListenableFuture<>(result); Action action;/*from www. j a v a 2s. co m*/ Future<Collection<Tuple>> future = new DefiniteFuture<>(tuples); ListeningExecutorService service = MoreExecutors.listeningDecorator(Executors.newSingleThreadExecutor()); while ((action = queue.poll()) != null) future = service.submit(InjectionLogger.injectLogger(result, action.new Task(future))); final ListenableFuture<Collection<Tuple>> lastFuture = (ListenableFuture<Collection<Tuple>>) future; return new ListenableFuture<Result>() { @Override public boolean isDone() { return lastFuture.isDone(); } @Override public boolean isCancelled() { return lastFuture.isCancelled(); } @Override public Result get(long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException { result.setTuples(lastFuture.get(timeout, unit)); return result; } @Override public Result get() throws InterruptedException, ExecutionException { result.setTuples(lastFuture.get()); return result; } @Override public boolean cancel(boolean mayInterruptIfRunning) { return lastFuture.cancel(mayInterruptIfRunning); } @Override public void addListener(Runnable listener, Executor executor) { lastFuture.addListener(listener, executor); } }; }
From source file:com.google.digitalcoin.core.PeerGroup.java
/** * <p>Given a transaction, sends it un-announced to one peer and then waits for it to be received back from other * peers. Once all connected peers have announced the transaction, the future will be completed. If anything goes * wrong the exception will be thrown when get() is called, or you can receive it via a callback on the * {@link ListenableFuture}. This method returns immediately, so if you want it to block just call get() on the * result.</p>/*from w w w . jav a 2s .co m*/ * * <p>Note that if the PeerGroup is limited to only one connection (discovery is not activated) then the future * will complete as soon as the transaction was successfully written to that peer.</p> * * <p>Other than for sending your own transactions, this method is useful if you have received a transaction from * someone and want to know that it's valid. It's a bit of a weird hack because the current version of the Digitalcoin * protocol does not inform you if you send an invalid transaction. Because sending bad transactions counts towards * your DoS limit, be careful with relaying lots of unknown transactions. Otherwise you might get kicked off the * network.</p> * * <p>The transaction won't be sent until there are at least minConnections active connections available. * A good choice for proportion would be between 0.5 and 0.8 but if you want faster transmission during initial * bringup of the peer group you can lower it.</p> */ public ListenableFuture<Transaction> broadcastTransaction(final Transaction tx, final int minConnections) { final SettableFuture<Transaction> future = SettableFuture.create(); log.info("Waiting for {} peers required for broadcast ...", minConnections); ListenableFuture<PeerGroup> peerAvailabilityFuture = waitForPeers(minConnections); peerAvailabilityFuture.addListener(new Runnable() { public void run() { // We now have enough connected peers to send the transaction. // This can be called immediately if we already have enough. Otherwise it'll be called from a peer // thread. // Pick a peer to be the lucky recipient of our tx. final Peer somePeer = peers.get(0); log.info("broadcastTransaction: Enough peers, adding {} to the memory pool and sending to {}", tx.getHashAsString(), somePeer); final Transaction pinnedTx = memoryPool.seen(tx, somePeer.getAddress()); // Prepare to send the transaction by adding a listener that'll be called when confidence changes. // Only bother with this if we might actually hear back: if (minConnections > 1) tx.getConfidence().addEventListener(new TransactionConfidence.Listener() { public void onConfidenceChanged(Transaction tx) { // The number of peers that announced this tx has gone up. // Thread safe - this can run in parallel. final TransactionConfidence conf = tx.getConfidence(); int numSeenPeers = conf.numBroadcastPeers(); boolean mined = conf .getConfidenceType() != TransactionConfidence.ConfidenceType.NOT_SEEN_IN_CHAIN; log.info("broadcastTransaction: TX {} seen by {} peers{}", new Object[] { pinnedTx.getHashAsString(), numSeenPeers, mined ? " and mined" : "" }); if (!(numSeenPeers >= minConnections || mined)) return; // We've seen the min required number of peers announce the transaction, or it was included // in a block. Normally we'd expect to see it fully propagate before it gets mined, but // it can be that a block is solved very soon after broadcast, and it's also possible that // due to version skew and changes in the relay rules our transaction is not going to // fully propagate yet can get mined anyway. // // Note that we can't wait for the current number of connected peers right now because we // could have added more peers after the broadcast took place, which means they won't // have seen the transaction. In future when peers sync up their memory pools after they // connect we could come back and change this. // // OK, now tell the wallet about the transaction. If the wallet created the transaction then // it already knows and will ignore this. If it's a transaction we received from // somebody else via a side channel and are now broadcasting, this will put it into the // wallet now we know it's valid. for (Wallet wallet : wallets) { try { // Assumption here is there are no dependencies of the created transaction. // // We may end up with two threads trying to do this in parallel - the wallet will // ignore whichever one loses the race. wallet.receivePending(pinnedTx, null); } catch (Throwable t) { future.setException(t); // RE-ENTRANCY POINT return; } } // We're done! It's important that the PeerGroup lock is not held (by this thread) at this // point to avoid triggering inversions when the Future completes. log.info("broadcastTransaction: {} complete", pinnedTx.getHashAsString()); tx.getConfidence().removeEventListener(this); future.set(pinnedTx); // RE-ENTRANCY POINT } }); // Satoshis code sends an inv in this case and then lets the peer request the tx data. We just // blast out the TX here for a couple of reasons. Firstly it's simpler: in the case where we have // just a single connection we don't have to wait for getdata to be received and handled before // completing the future in the code immediately below. Secondly, it's faster. The reason the // Satoshi client sends an inv is privacy - it means you can't tell if the peer originated the // transaction or not. However, we are not a fully validating node and this is advertised in // our version message, as SPV nodes cannot relay it doesn't give away any additional information // to skip the inv here - we wouldn't send invs anyway. // // TODO: The peer we picked might be dead by now. If we can't write the message, pick again and retry. ChannelFuture sendComplete = somePeer.sendMessage(pinnedTx); // If we've been limited to talk to only one peer, we can't wait to hear back because the // remote peer won't tell us about transactions we just announced to it for obvious reasons. // So we just have to assume we're done, at that point. This happens when we're not given // any peer discovery source and the user just calls connectTo() once. if (minConnections == 1) { sendComplete.addListener(new ChannelFutureListener() { public void operationComplete(ChannelFuture _) throws Exception { for (Wallet wallet : wallets) { try { // Assumption here is there are no dependencies of the created transaction. wallet.receivePending(pinnedTx, null); } catch (Throwable t) { future.setException(t); return; } } future.set(pinnedTx); } }); } } }, MoreExecutors.sameThreadExecutor()); return future; }
From source file:com.facebook.buck.util.concurrent.ResourcePool.java
/** * @param executorService where to perform the resource processing. Should really be a "real" * executor (not a directExecutor). * @return a {@link ListenableFuture} containing the result of the processing. The future will be * cancelled if the {@link ResourcePool#close()} method is called. *///from ww w .ja v a 2s . c o m public synchronized <T> ListenableFuture<T> scheduleOperationWithResource(ThrowingFunction<R, T> withResource, final ListeningExecutorService executorService) { Preconditions.checkState(!closing.get()); final ListenableFuture<T> futureWork = Futures.transformAsync(initialSchedule(), new AsyncFunction<Void, T>() { @Override public ListenableFuture<T> apply(Void input) throws Exception { Either<R, ListenableFuture<Void>> resourceRequest = requestResource(); if (resourceRequest.isLeft()) { R resource = resourceRequest.getLeft(); boolean resourceIsDefunct = false; try { return Futures.immediateFuture(withResource.apply(resource)); } catch (Exception e) { resourceIsDefunct = (resourceUsageErrorPolicy == ResourceUsageErrorPolicy.RETIRE); throw e; } finally { returnResource(resource, resourceIsDefunct); } } else { return Futures.transformAsync(resourceRequest.getRight(), this, executorService); } } }, executorService); pendingWork.add(futureWork); futureWork.addListener(() -> { synchronized (ResourcePool.this) { pendingWork.remove(futureWork); } }, executorService); // If someone else calls cancel on `futureWork` it makes it impossible to wait for that future // to finish using the resource, which then makes shutdown code exit too early. return Futures.nonCancellationPropagating(futureWork); }
From source file:com.google.NithPoints.core.PeerGroup.java
/** * <p>Given a transaction, sends it un-announced to one peer and then waits for it to be received back from other * peers. Once all connected peers have announced the transaction, the future will be completed. If anything goes * wrong the exception will be thrown when get() is called, or you can receive it via a callback on the * {@link ListenableFuture}. This method returns immediately, so if you want it to block just call get() on the * result.</p>//from w w w .j av a2s . c o m * * <p>Note that if the PeerGroup is limited to only one connection (discovery is not activated) then the future * will complete as soon as the transaction was successfully written to that peer.</p> * * <p>Other than for sending your own transactions, this method is useful if you have received a transaction from * someone and want to know that it's valid. It's a bit of a weird hack because the current version of the NithPoints * protocol does not inform you if you send an invalid transaction. Because sending bad transactions counts towards * your DoS limit, be careful with relaying lots of unknown transactions. Otherwise you might get kicked off the * network.</p> * * <p>The transaction won't be sent until there are at least minConnections active connections available. * A good choice for proportion would be between 0.5 and 0.8 but if you want faster transmission during initial * bringup of the peer group you can lower it.</p> */ public ListenableFuture<Transaction> broadcastTransaction(final Transaction tx, final int minConnections) { final SettableFuture<Transaction> future = SettableFuture.create(); log.info("Waiting for {} peers required for broadcast ...", minConnections); ListenableFuture<PeerGroup> peerAvailabilityFuture = waitForPeers(minConnections); peerAvailabilityFuture.addListener(new Runnable() { public void run() { // We now have enough connected peers to send the transaction. // This can be called immediately if we already have enough. Otherwise it'll be called from a peer // thread. // Pick a peer to be the lucky recipient of our tx. This can race if the peer we pick dies immediately. final Peer somePeer; lock.lock(); try { somePeer = peers.get(0); } finally { lock.unlock(); } log.info("broadcastTransaction: Enough peers, adding {} to the memory pool and sending to {}", tx.getHashAsString(), somePeer); final Transaction pinnedTx = memoryPool.seen(tx, somePeer.getAddress()); // Prepare to send the transaction by adding a listener that'll be called when confidence changes. // Only bother with this if we might actually hear back: if (minConnections > 1) tx.getConfidence().addEventListener(new TransactionConfidence.Listener() { public void onConfidenceChanged(Transaction tx) { // The number of peers that announced this tx has gone up. // Thread safe - this can run in parallel. final TransactionConfidence conf = tx.getConfidence(); int numSeenPeers = conf.numBroadcastPeers(); boolean mined = conf .getConfidenceType() != TransactionConfidence.ConfidenceType.NOT_SEEN_IN_CHAIN; log.info("broadcastTransaction: TX {} seen by {} peers{}", new Object[] { pinnedTx.getHashAsString(), numSeenPeers, mined ? " and mined" : "" }); if (!(numSeenPeers >= minConnections || mined)) return; // We've seen the min required number of peers announce the transaction, or it was included // in a block. Normally we'd expect to see it fully propagate before it gets mined, but // it can be that a block is solved very soon after broadcast, and it's also possible that // due to version skew and changes in the relay rules our transaction is not going to // fully propagate yet can get mined anyway. // // Note that we can't wait for the current number of connected peers right now because we // could have added more peers after the broadcast took place, which means they won't // have seen the transaction. In future when peers sync up their memory pools after they // connect we could come back and change this. // // OK, now tell the wallet about the transaction. If the wallet created the transaction then // it already knows and will ignore this. If it's a transaction we received from // somebody else via a side channel and are now broadcasting, this will put it into the // wallet now we know it's valid. for (Wallet wallet : wallets) { try { // Assumption here is there are no dependencies of the created transaction. // // We may end up with two threads trying to do this in parallel - the wallet will // ignore whichever one loses the race. wallet.receivePending(pinnedTx, null); } catch (Throwable t) { future.setException(t); // RE-ENTRANCY POINT return; } } // We're done! It's important that the PeerGroup lock is not held (by this thread) at this // point to avoid triggering inversions when the Future completes. log.info("broadcastTransaction: {} complete", pinnedTx.getHashAsString()); tx.getConfidence().removeEventListener(this); future.set(pinnedTx); // RE-ENTRANCY POINT } }); // Satoshis code sends an inv in this case and then lets the peer request the tx data. We just // blast out the TX here for a couple of reasons. Firstly it's simpler: in the case where we have // just a single connection we don't have to wait for getdata to be received and handled before // completing the future in the code immediately below. Secondly, it's faster. The reason the // Satoshi client sends an inv is privacy - it means you can't tell if the peer originated the // transaction or not. However, we are not a fully validating node and this is advertised in // our version message, as SPV nodes cannot relay it doesn't give away any additional information // to skip the inv here - we wouldn't send invs anyway. // // TODO: The peer we picked might be dead by now. If we can't write the message, pick again and retry. ChannelFuture sendComplete = somePeer.sendMessage(pinnedTx); // If we've been limited to talk to only one peer, we can't wait to hear back because the // remote peer won't tell us about transactions we just announced to it for obvious reasons. // So we just have to assume we're done, at that point. This happens when we're not given // any peer discovery source and the user just calls connectTo() once. if (minConnections == 1) { sendComplete.addListener(new ChannelFutureListener() { public void operationComplete(ChannelFuture _) throws Exception { for (Wallet wallet : wallets) { try { // Assumption here is there are no dependencies of the created transaction. wallet.receivePending(pinnedTx, null); } catch (Throwable t) { future.setException(t); return; } } future.set(pinnedTx); } }); } } }, MoreExecutors.sameThreadExecutor()); return future; }
From source file:com.google.logicoin.core.PeerGroup.java
/** * <p>Given a transaction, sends it un-announced to one peer and then waits for it to be received back from other * peers. Once all connected peers have announced the transaction, the future will be completed. If anything goes * wrong the exception will be thrown when get() is called, or you can receive it via a callback on the * {@link ListenableFuture}. This method returns immediately, so if you want it to block just call get() on the * result.</p>//from w w w. j a v a 2 s. c o m * * <p>Note that if the PeerGroup is limited to only one connection (discovery is not activated) then the future * will complete as soon as the transaction was successfully written to that peer.</p> * * <p>Other than for sending your own transactions, this method is useful if you have received a transaction from * someone and want to know that it's valid. It's a bit of a weird hack because the current version of the Bitcoin * protocol does not inform you if you send an invalid transaction. Because sending bad transactions counts towards * your DoS limit, be careful with relaying lots of unknown transactions. Otherwise you might get kicked off the * network.</p> * * <p>The transaction won't be sent until there are at least minConnections active connections available. * A good choice for proportion would be between 0.5 and 0.8 but if you want faster transmission during initial * bringup of the peer group you can lower it.</p> */ public ListenableFuture<Transaction> broadcastTransaction(final Transaction tx, final int minConnections) { final SettableFuture<Transaction> future = SettableFuture.create(); log.info("Waiting for {} peers required for broadcast ...", minConnections); ListenableFuture<PeerGroup> peerAvailabilityFuture = waitForPeers(minConnections); peerAvailabilityFuture.addListener(new Runnable() { public void run() { // We now have enough connected peers to send the transaction. // This can be called immediately if we already have enough. Otherwise it'll be called from a peer // thread. // Pick a peer to be the lucky recipient of our tx. This can race if the peer we pick dies immediately. final Peer somePeer; lock.lock(); try { somePeer = peers.get(0); } finally { lock.unlock(); } log.info("broadcastTransaction: Enough peers, adding {} to the memory pool and sending to {}", tx.getHashAsString(), somePeer); final Transaction pinnedTx = memoryPool.seen(tx, somePeer.getAddress()); // Prepare to send the transaction by adding a listener that'll be called when confidence changes. // Only bother with this if we might actually hear back: if (minConnections > 1) tx.getConfidence().addEventListener(new TransactionConfidence.Listener() { public void onConfidenceChanged(Transaction tx, TransactionConfidence.Listener.ChangeReason reason) { // The number of peers that announced this tx has gone up. final TransactionConfidence conf = tx.getConfidence(); int numSeenPeers = conf.numBroadcastPeers(); boolean mined = tx.getAppearsInHashes() != null; log.info("broadcastTransaction: TX {} seen by {} peers{}", new Object[] { pinnedTx.getHashAsString(), numSeenPeers, mined ? " and mined" : "" }); if (!(numSeenPeers >= minConnections || mined)) return; // We've seen the min required number of peers announce the transaction, or it was included // in a block. Normally we'd expect to see it fully propagate before it gets mined, but // it can be that a block is solved very soon after broadcast, and it's also possible that // due to version skew and changes in the relay rules our transaction is not going to // fully propagate yet can get mined anyway. // // Note that we can't wait for the current number of connected peers right now because we // could have added more peers after the broadcast took place, which means they won't // have seen the transaction. In future when peers sync up their memory pools after they // connect we could come back and change this. // // OK, now tell the wallet about the transaction. If the wallet created the transaction then // it already knows and will ignore this. If it's a transaction we received from // somebody else via a side channel and are now broadcasting, this will put it into the // wallet now we know it's valid. for (Wallet wallet : wallets) { try { // Assumption here is there are no dependencies of the created transaction. // // We may end up with two threads trying to do this in parallel - the wallet will // ignore whichever one loses the race. wallet.receivePending(pinnedTx, null); } catch (Throwable t) { future.setException(t); // RE-ENTRANCY POINT return; } } // We're done! It's important that the PeerGroup lock is not held (by this thread) at this // point to avoid triggering inversions when the Future completes. log.info("broadcastTransaction: {} complete", pinnedTx.getHashAsString()); tx.getConfidence().removeEventListener(this); future.set(pinnedTx); // RE-ENTRANCY POINT } }); // Satoshis code sends an inv in this case and then lets the peer request the tx data. We just // blast out the TX here for a couple of reasons. Firstly it's simpler: in the case where we have // just a single connection we don't have to wait for getdata to be received and handled before // completing the future in the code immediately below. Secondly, it's faster. The reason the // Satoshi client sends an inv is privacy - it means you can't tell if the peer originated the // transaction or not. However, we are not a fully validating node and this is advertised in // our version message, as SPV nodes cannot relay it doesn't give away any additional information // to skip the inv here - we wouldn't send invs anyway. // // TODO: The peer we picked might be dead by now. If we can't write the message, pick again and retry. ChannelFuture sendComplete = somePeer.sendMessage(pinnedTx); // If we've been limited to talk to only one peer, we can't wait to hear back because the // remote peer won't tell us about transactions we just announced to it for obvious reasons. // So we just have to assume we're done, at that point. This happens when we're not given // any peer discovery source and the user just calls connectTo() once. if (minConnections == 1) { sendComplete.addListener(new ChannelFutureListener() { public void operationComplete(ChannelFuture _) throws Exception { for (Wallet wallet : wallets) { try { // Assumption here is there are no dependencies of the created transaction. wallet.receivePending(pinnedTx, null); } catch (Throwable t) { future.setException(t); return; } } future.set(pinnedTx); } }); } } }, Threading.SAME_THREAD); return future; }
From source file:com.facebook.buck.distributed.build_slave.ThriftCoordinatorServer.java
public ThriftCoordinatorServer(OptionalInt port, ListenableFuture<BuildTargetsQueue> queue, StampedeId stampedeId, EventListener eventListener, CoordinatorBuildRuleEventsPublisher coordinatorBuildRuleEventsPublisher, MinionHealthTracker minionHealthTracker, DistBuildService distBuildService, MinionCountProvider minionCountProvider, Optional<String> coordinatorMinionId, boolean releasingMinionsEarlyEnabled) { this.eventListener = eventListener; this.stampedeId = stampedeId; this.coordinatorBuildRuleEventsPublisher = coordinatorBuildRuleEventsPublisher; this.minionHealthTracker = minionHealthTracker; this.distBuildService = distBuildService; this.minionCountProvider = minionCountProvider; this.coordinatorMinionId = coordinatorMinionId; this.releasingMinionsEarlyEnabled = releasingMinionsEarlyEnabled; this.lock = new Object(); this.exitCodeFuture = new CompletableFuture<>(); this.chromeTraceTracker = new DistBuildTraceTracker(stampedeId); this.port = port; this.handler = new IdleCoordinatorService(); this.deadMinions = new HashSet<>(); CoordinatorServiceHandler handlerWrapper = new CoordinatorServiceHandler(); this.processor = new CoordinatorService.Processor<>(handlerWrapper); queue.addListener(() -> switchToActiveModeOrFail(queue), MoreExecutors.directExecutor()); }