001    /*
002     * Copyright 2005 Stephen J. McConnell.
003     *
004     * Licensed  under the  Apache License,  Version 2.0  (the "License");
005     * you may not use  this file  except in  compliance with the License.
006     * You may obtain a copy of the License at
007     *
008     *   http://www.apache.org/licenses/LICENSE-2.0
009     *
010     * Unless required by applicable law or agreed to in writing, software
011     * distributed  under the  License is distributed on an "AS IS" BASIS,
012     * WITHOUT  WARRANTIES OR CONDITIONS  OF ANY KIND, either  express  or
013     * implied.
014     *
015     * See the License for the specific language governing permissions and
016     * limitations under the License.
017     */
018    
019    package net.dpml.station.server;
020    
021    import java.util.EventObject;
022    import java.util.EventListener;
023    import java.rmi.server.UnicastRemoteObject;
024    import java.rmi.RemoteException;
025    
026    import net.dpml.util.Logger;
027    
028    
029    /**
030     * A abstract base class that established an event queue and handles event dispatch 
031     * operations for listeners declared in a class extending this base class.
032     *
033     * @author <a href="http://www.dpml.net">Digital Product Meta Library</a>
034     * @version 1.0.5
035     */
036    public abstract class UnicastEventSource extends UnicastRemoteObject
037    {
038       /**
039        * Registered event listeners.
040        */
041        private EventListener[] m_listeners = new EventListener[0];
042    
043        private Logger m_logger;
044    
045       /**
046        * Internal synchronization lock.
047        */
048        private final Object m_lock = new Object();
049    
050       /**
051        * Creation of a new event producer.
052        * @param logger the logging channel
053        * @exception RemoteException if a remote exception occurs
054        */
055        public UnicastEventSource( Logger logger ) throws RemoteException
056        {
057            super();
058    
059            m_logger = logger;
060        }
061    
062       /**
063        * Return the assigned logging channel.
064        * @return the logging channel
065        */
066        protected Logger getLogger()
067        {
068            return m_logger;
069        }
070    
071       /**
072        * Abstract operation to be implemented by classes extending this base class.
073        * An implementation is reposible for the posting of the event to associated 
074        * listeners.  Event posting will be executed under a separate thread to the 
075        * thread that initiated the event post.
076        *
077        * @param event the event to process
078        */
079        protected abstract void processEvent( EventObject event );
080    
081       /**
082        * Add a listener to the set of listeners handled by this producer.
083        * @param listener the event listener
084        */
085        protected void addListener( EventListener listener ) 
086        {
087            if( null == listener )
088            {
089                throw new NullPointerException( "listener" );
090            }
091    
092            synchronized( m_lock ) 
093            {
094                Object[] old = m_listeners;
095                m_listeners = new EventListener[ old.length + 1 ];
096                System.arraycopy( old, 0, m_listeners, 0, old.length );
097                m_listeners[old.length] = listener;
098            }
099        }
100    
101       /**
102        * Remove a listener to the set of listeners handled by this producer.
103        * @param listener the event listener
104        */
105        protected void removeListener( EventListener listener )
106        {
107            if( null == listener )
108            {
109                throw new NullPointerException( "listener" );
110            }
111    
112            synchronized( m_lock ) 
113            {
114                if( m_listeners.length == 0 )
115                {
116                    throw new IllegalArgumentException( "Listener not registered." );
117                }
118                // create the copy
119                EventListener[] replacement = new EventListener[ m_listeners.length - 1 ];
120                // copy listeners from 0 up to the listener being removed
121                int i=0;
122                while( i < replacement.length && m_listeners[i] != listener )
123                {
124                    replacement[i] = m_listeners[i++];
125                }
126                // check that the listener has been located
127                if( i == replacement.length &&  m_listeners[i] != listener )
128                {
129                    throw new IllegalArgumentException( "Listener not registered." );
130                }
131                // complete the copy operation
132                while( i < replacement.length )
133                {
134                    replacement[i] = m_listeners[++i];
135                }
136                // commit the copy
137                m_listeners = replacement;
138            }
139        }
140    
141       /**
142        * Return this node's preference/node change listeners.  Even though
143        * we're using a copy-on-write lists, we use synchronized accessors to
144        * ensure information transmission from the writing thread to the
145        * reading thread.
146        * @return the event listener array
147        */
148        protected EventListener[] listeners() 
149        {
150            synchronized( m_lock ) 
151            {
152                return m_listeners;
153            }
154        }
155    
156       /**
157        * Enqueue an event for delivery to registered
158        * listeners unless there are no registered
159        * listeners.
160        * @param event the event to enqueue
161        */
162        protected void enqueueEvent( EventObject event )
163        {
164            if( m_listeners.length != 0 ) 
165            {
166                RemoteStation.enqueueEvent( event );
167            }
168        }
169    }