| /* |
| * Copyright 2013-present Facebook, Inc. |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); you may |
| * not use this file except in compliance with the License. You may obtain |
| * a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT |
| * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the |
| * License for the specific language governing permissions and limitations |
| * under the License. |
| */ |
| package com.facebook.buck.event; |
| |
| import com.facebook.buck.log.CommandThreadFactory; |
| import com.facebook.buck.log.Logger; |
| import com.facebook.buck.model.BuildId; |
| import com.facebook.buck.timing.Clock; |
| import com.facebook.buck.util.concurrent.MoreExecutors; |
| import com.google.common.annotations.VisibleForTesting; |
| import com.google.common.base.Joiner; |
| import com.google.common.base.Supplier; |
| import com.google.common.eventbus.AsyncEventBus; |
| |
| import java.io.Closeable; |
| import java.io.IOException; |
| import java.util.concurrent.ExecutorService; |
| import java.util.concurrent.TimeUnit; |
| |
| /** |
| * Thin wrapper around guava event bus. |
| */ |
| public class BuckEventBus implements Closeable { |
| |
| private static final Logger LOG = Logger.get(BuckEventBus.class); |
| |
| public static final int DEFAULT_SHUTDOWN_TIMEOUT_MS = 15000; |
| |
| private static final Supplier<Long> DEFAULT_THREAD_ID_SUPPLIER = new Supplier<Long>() { |
| @Override |
| public Long get() { |
| return Thread.currentThread().getId(); |
| } |
| }; |
| |
| private final Clock clock; |
| private final ExecutorService executorService; |
| private final AsyncEventBus eventBus; |
| private final Supplier<Long> threadIdSupplier; |
| private final BuildId buildId; |
| private final int shutdownTimeoutMillis; |
| |
| public BuckEventBus(Clock clock, BuildId buildId) { |
| this(clock, |
| MoreExecutors.newSingleThreadExecutor( |
| new CommandThreadFactory(BuckEventBus.class.getSimpleName())), |
| buildId, |
| DEFAULT_SHUTDOWN_TIMEOUT_MS); |
| } |
| |
| @VisibleForTesting |
| BuckEventBus( |
| Clock clock, |
| ExecutorService executorService, |
| BuildId buildId, |
| int shutdownTimeoutMillis) { |
| this.clock = clock; |
| this.executorService = executorService; |
| this.eventBus = new AsyncEventBus("buck-build-events", executorService); |
| this.threadIdSupplier = DEFAULT_THREAD_ID_SUPPLIER; |
| this.buildId = buildId; |
| this.shutdownTimeoutMillis = shutdownTimeoutMillis; |
| } |
| |
| public void post(BuckEvent event) { |
| timestamp(event); |
| eventBus.post(event); |
| } |
| |
| public void logVerboseAndPost(Logger logger, BuckEvent event) { |
| logger.verbose("%s", event); |
| post(event); |
| } |
| |
| public void logDebugAndPost(Logger logger, BuckEvent event) { |
| logger.debug("%s", event); |
| post(event); |
| } |
| |
| /** |
| * Post event to the EventBus using the timestamp given by atTime. |
| */ |
| public void post(BuckEvent event, BuckEvent atTime) { |
| event.configure(atTime.getTimestamp(), atTime.getNanoTime(), threadIdSupplier.get(), buildId); |
| eventBus.post(event); |
| } |
| |
| public void register(Object object) { |
| eventBus.register(object); |
| } |
| |
| public void unregister(Object object) { |
| eventBus.unregister(object); |
| } |
| |
| @VisibleForTesting |
| AsyncEventBus getEventBus() { |
| return eventBus; |
| } |
| |
| @VisibleForTesting |
| Clock getClock() { |
| return clock; |
| } |
| |
| @VisibleForTesting |
| Supplier<Long> getThreadIdSupplier() { |
| return threadIdSupplier; |
| } |
| |
| /** |
| * An id that every event posted to this event bus will share. For long-running processes, like |
| * the daemon, the build id makes it possible to distinguish when events come from different |
| * invocations of Buck. |
| * <p> |
| * In practice, this should be a short string, because it may be sent over the wire frequently. |
| */ |
| public BuildId getBuildId() { |
| return buildId; |
| } |
| |
| /** |
| * {@link ExecutorService#awaitTermination(long, java.util.concurrent.TimeUnit)} is called |
| * to wait for events which have been posted, but which have been queued by the |
| * {@link AsyncEventBus}, to be delivered. This allows listeners to record or report as much |
| * information as possible. This aids debugging when close is called during exception processing. |
| */ |
| @Override |
| public void close() throws IOException { |
| executorService.shutdown(); |
| try { |
| if (!executorService.awaitTermination(shutdownTimeoutMillis, TimeUnit.MILLISECONDS)) { |
| LOG.warn(Joiner.on(System.lineSeparator()).join( |
| "The BuckEventBus failed to shut down within the standard timeout.", |
| "Your build might have succeeded, but some messages were probably lost.", |
| "Here's some debugging information:", |
| executorService.toString())); |
| executorService.shutdownNow(); |
| } |
| } catch (InterruptedException e) { |
| Thread.currentThread().interrupt(); |
| } |
| } |
| |
| /** |
| * Timestamp event. A timestamped event cannot subsequently being posted and is useful only to |
| * pass its timestamp on to another posted event. |
| */ |
| public void timestamp(BuckEvent event) { |
| event.configure(clock.currentTimeMillis(), clock.nanoTime(), threadIdSupplier.get(), buildId); |
| } |
| } |