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

io.github.pustike.eventbus.Subscriber Maven / Gradle / Ivy

The newest version!
/*
 * Copyright (C) 2016-2018 the original author or 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
 *
 * https://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 io.github.pustike.eventbus;

import java.lang.ref.WeakReference;
import java.lang.reflect.InaccessibleObjectException;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Objects;

/**
 * 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, int registryKey) { Subscribe subscribe = method.getAnnotation(Subscribe.class); return subscribe != null && subscribe.threadSafe() ? new Subscriber(bus, listener, method, registryKey) : new SynchronizedSubscriber(bus, listener, method, registryKey); } /** The event bus this subscriber belongs to. */ private final EventBus eventBus; /** The object with the subscriber method. */ private final Object listener; /** Subscriber method. */ private final Method method; /** The pre-computed hash code. */ private final int hashCode; /** The parameter hashCode of this subscriber used by the registry. */ final int registryKey; private Subscriber(EventBus eventBus, Object listener, Method method, int registryKey) { this.eventBus = Objects.requireNonNull(eventBus); this.listener = Objects.requireNonNull(listener); if (!method.trySetAccessible()) { throw new InaccessibleObjectException("couldn't enable access to method: " + method); } this.method = method; this.registryKey = registryKey; this.hashCode = computeHashCode(); } /** * Dispatches {@code event} to this subscriber using the proper executor. */ final void dispatchEvent(final Object event) { eventBus.executor().execute(() -> { try { invokeSubscriberMethod(event); } catch (InvocationTargetException e) { // do not publish if an exception occurs in the exception event handler if (!(event instanceof ExceptionEvent)) { eventBus.publish(new ExceptionEvent(eventBus, getTarget(), method, event, e.getCause())); } } }); } /** * Invokes the subscriber method. This method can be overridden to make the invocation synchronized. */ void invokeSubscriberMethod(Object event) throws InvocationTargetException { try { Object target = getTarget(); if (target == null) {// if the target object is no longer available, eventBus.unregister(this); // un-subscribe this subscriber! return; } method.invoke(target, 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; } } Object getTarget() { return listener instanceof WeakReference ? ((WeakReference) listener).get() : listener; } private int computeHashCode() { return (31 + method.hashCode()) * 31 + System.identityHashCode(getTarget()); } @Override public final int hashCode() { return hashCode; } @Override public final boolean equals(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 getTarget() == that.getTarget() && method.equals(that.method); } return false; } /** * Subscriber that synchronizes invocations of a method to ensure that only one thread may enter the method at a * time. */ static final class SynchronizedSubscriber extends Subscriber { private SynchronizedSubscriber(EventBus bus, Object target, Method method, int registryKey) { super(bus, target, method, registryKey); } @Override void invokeSubscriberMethod(Object event) throws InvocationTargetException { synchronized (this) { super.invokeSubscriberMethod(event); } } } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy