All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.google.common.eventbus.Subscriber Maven / Gradle / Ivy

/*
 * Copyright (C) 2014 The Guava Authors
 *
 * 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.google.common.eventbus;

import static com.google.common.base.Preconditions.checkNotNull;

import com.google.common.annotations.VisibleForTesting;
import com.google.j2objc.annotations.Weak;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.concurrent.Executor;
import org.checkerframework.checker.nullness.compatqual.NullableDecl;

/**
 * A subscriber method on a specific object, plus the executor that should be used for dispatching
 * events to it.
 *
 * 

Two subscribers are equivalent when they refer to the same method on the same object (not * class). This property is used to ensure that no subscriber method is registered more than once. * * @author Colin Decker */ class Subscriber { /** Creates a {@code Subscriber} for {@code method} on {@code listener}. */ static Subscriber create(EventBus bus, Object listener, Method method) { return isDeclaredThreadSafe(method) ? new Subscriber(bus, listener, method) : new SynchronizedSubscriber(bus, listener, method); } /** The event bus this subscriber belongs to. */ @Weak private EventBus bus; /** The object with the subscriber method. */ @VisibleForTesting final Object target; /** Subscriber method. */ private final Method method; /** Executor to use for dispatching events to this subscriber. */ private final Executor executor; private Subscriber(EventBus bus, Object target, Method method) { this.bus = bus; this.target = checkNotNull(target); this.method = method; method.setAccessible(true); this.executor = bus.executor(); } /** Dispatches {@code event} to this subscriber using the proper executor. */ final void dispatchEvent(final Object event) { executor.execute( new Runnable() { @Override public void run() { try { invokeSubscriberMethod(event); } catch (InvocationTargetException e) { bus.handleSubscriberException(e.getCause(), context(event)); } } }); } /** * Invokes the subscriber method. This method can be overridden to make the invocation * synchronized. */ @VisibleForTesting void invokeSubscriberMethod(Object event) throws InvocationTargetException { try { method.invoke(target, checkNotNull(event)); } catch (IllegalArgumentException e) { throw new Error("Method rejected target/argument: " + event, e); } catch (IllegalAccessException e) { throw new Error("Method became inaccessible: " + event, e); } catch (InvocationTargetException e) { if (e.getCause() instanceof Error) { throw (Error) e.getCause(); } throw e; } } /** Gets the context for the given event. */ private SubscriberExceptionContext context(Object event) { return new SubscriberExceptionContext(bus, event, target, method); } @Override public final int hashCode() { return (31 + method.hashCode()) * 31 + System.identityHashCode(target); } @Override public final boolean equals(@NullableDecl Object obj) { if (obj instanceof Subscriber) { Subscriber that = (Subscriber) obj; // Use == so that different equal instances will still receive events. // We only guard against the case that the same object is registered // multiple times return target == that.target && method.equals(that.method); } return false; } /** * Checks whether {@code method} is thread-safe, as indicated by the presence of the {@link * AllowConcurrentEvents} annotation. */ private static boolean isDeclaredThreadSafe(Method method) { return method.getAnnotation(AllowConcurrentEvents.class) != null; } /** * Subscriber that synchronizes invocations of a method to ensure that only one thread may enter * the method at a time. */ @VisibleForTesting static final class SynchronizedSubscriber extends Subscriber { private SynchronizedSubscriber(EventBus bus, Object target, Method method) { super(bus, target, method); } @Override void invokeSubscriberMethod(Object event) throws InvocationTargetException { synchronized (this) { super.invokeSubscriberMethod(event); } } } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy