package textbender.g.util; // Copyright 2006, Michael Allan. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Textbender Software"), to deal in the Textbender Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicence, and/or sell copies of the Textbender Software, and to permit persons to whom the Textbender Software is furnished to do so, subject to the following conditions: The preceding copyright notice and this permission notice shall be included in all copies or substantial portions of the Textbender Software. THE TEXTBENDER SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE TEXTBENDER SOFTWARE OR THE USE OR OTHER DEALINGS IN THE TEXTBENDER SOFTWARE. import textbender.g.lang.ThreadSafe; /** A remote event listener. * *

getSource() Behaviour of Events

*

* For an EventObject e passed to a remote listener, * e.{@linkplain java.util.EventObject#getSource() getSource}() * will typically return null. * This occurs because EventObject.source is a transient field, * and most event types (subclasses of EventObject) * do not bother to code its serialization. * Rather they allow it to become null after e is serialized and unserialized, * e.g. marshalled and unmarshalled through RMI, * as it usually is by the time it gets to the remote listener. *

*

* Some event types may differ in this behaviour, * especially those that are designed explicitly for remote listeners. * But note: serializing a remote reference * (as the event source often is, from the listener's perspective) * might involve garbage collection complications (see link below). * If it had to be serialized, it might be better to record it * as a String or something that identifies the source by type * (e.g. by class name) rather than by an instance reference. *

*

* In the typical case, the listener avoids e.getSource(). * If the source is needed at event handling time, * a reference is saved at registration time, beforehand. *

* * @see Is there any (ObjectStream sense) serializable remote ref in RMI? */ public @ThreadSafe interface EventListenerR extends java.rmi.Remote, java.util.EventListener { // ==================================================================================== /** A wrapper of a remote event listener that conforms it * to an equivalent local type, by handling any remote exceptions. * Used in cases where the remote type is otherwise identical to the local * (as PropertyChangeListenerR is to PropertyChangeListener). * It makes the remote instance assignment-compatible with the local type, * by catching and handling any remote exceptions during event dispatch. * Usually a {@linkplain textbender.g.lang.Catcher Catcher} is used for this purpose, * but it depends on the implementing subclass. */ public static @ThreadSafe abstract class WrapperL { /** Constructs a WrapperL. * * @param remoteListener underlying remote listener, * per {@linkplain #remoteListener() remoteListener}() */ protected WrapperL( R remoteListener ) { if( remoteListener == null ) throw new NullPointerException(); this.remoteListener = remoteListener; } // ------------------------------------------------------------------------------------ /** Underlying remote listener. It is never null. */ public final R remoteListener() { return remoteListener; } protected final R remoteListener; // - O b j e c t ---------------------------------------------------------------------- /** Returns true iff o wraps, or is, an 'equal' underlying remote listener. */ public @Override final boolean equals( Object o ) { final Object otherRemoteListener; if( o instanceof WrapperL ) otherRemoteListener = ((WrapperL)o).remoteListener; else otherRemoteListener = o; return remoteListener.equals( otherRemoteListener ); } /** Returns the same hash code as the underlying remote listener. */ public @Override final int hashCode() { return remoteListener.hashCode(); } /** Returns the same string as the underlying remote listener, * but prefixed by "local wrap of: ". */ public @Override final String toString() { return "local wrap of: " + remoteListener.toString(); } } }