001 /* 002 * Copyright 2006 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.util; 020 021 import java.util.EventObject; 022 import java.util.List; 023 import java.util.LinkedList; 024 025 import net.dpml.transit.monitor.LoggingAdapter; 026 027 /** 028 * A abstract base class that established an event queue and handles event dispatch 029 * operations for listeners declared in classes extending this base class. 030 * 031 * @author <a href="http://www.dpml.net">Digital Product Meta Library</a> 032 * @version 1.0.3 033 */ 034 public class EventQueue 035 { 036 // ------------------------------------------------------------------------ 037 // state 038 // ------------------------------------------------------------------------ 039 040 private final EventDispatchThread m_thread; 041 042 private final Logger m_logger; 043 044 private final List m_queue; 045 046 // ------------------------------------------------------------------------ 047 // constructor 048 // ------------------------------------------------------------------------ 049 050 /** 051 * Creation of a new event queue. 052 * @param category the name used to construct a logging channel 053 * @deprecated Use new EventQueue( logger, "Thread name" ) instead. 054 */ 055 public EventQueue( String category ) 056 { 057 this( category, "Event Dispatch Thread" ); 058 } 059 060 /** 061 * Creation of a new model. 062 * @param logger the assigned logging channel 063 * @exception NullPointerException if the supplied logging channel is null 064 * @deprecated Use new EventQueue( logger, "Thread name" ) instead. 065 */ 066 public EventQueue( Logger logger ) 067 throws NullPointerException 068 { 069 this( logger, "Event Dispatch Thread" ); 070 } 071 072 /** 073 * Creation of a new event queue. 074 * @param category the name used to construct a logging channel 075 * @param name the name to assign to the thread 076 */ 077 public EventQueue( String category, String name ) 078 { 079 this( getLoggerForCategory( category ), name ); 080 } 081 082 /** 083 * Creation of a new model. 084 * @param logger the assigned logging channel 085 * @param name the name to assign to the thread 086 * @exception NullPointerException if the supplied logging channel or 087 * thread name is null 088 */ 089 public EventQueue( Logger logger, String name ) 090 throws NullPointerException 091 { 092 if( null == logger ) 093 { 094 throw new NullPointerException( "logger" ); 095 } 096 if( null == name ) 097 { 098 throw new NullPointerException( "name" ); 099 } 100 m_logger = logger; 101 m_queue = new LinkedList(); 102 m_thread = new EventDispatchThread(); 103 m_thread.setName( name ); 104 m_thread.setDaemon( true ); 105 m_thread.start(); 106 } 107 108 // ------------------------------------------------------------------------ 109 // EventQueue 110 // ------------------------------------------------------------------------ 111 112 /** 113 * Terminate the dispatch thread. 114 */ 115 public synchronized void terminateDispatchThread() 116 { 117 if( null != m_thread ) 118 { 119 m_thread.dispose(); 120 } 121 } 122 123 /** 124 * Return the assigned logging channel. 125 * @return the logging channel 126 */ 127 private Logger getLogger() 128 { 129 return m_logger; 130 } 131 132 /** 133 * A single background thread ("the event notification thread") monitors 134 * the event queue and delivers events that are placed on the queue. 135 */ 136 private class EventDispatchThread extends Thread 137 { 138 private boolean m_continue = true; 139 140 void dispose() 141 { 142 synchronized( m_queue ) 143 { 144 m_continue = false; 145 m_queue.notify(); 146 } 147 } 148 149 public void run() 150 { 151 while( m_continue ) 152 { 153 // Wait on m_queue till an event is present 154 EventObject event = null; 155 synchronized( m_queue ) 156 { 157 try 158 { 159 while( m_continue && m_queue.isEmpty() ) 160 { 161 m_queue.wait(); 162 } 163 if ( !m_continue ) 164 { 165 break; 166 } 167 Object object = m_queue.remove( 0 ); 168 try 169 { 170 event = (EventObject) object; 171 } 172 catch( ClassCastException cce ) 173 { 174 final String error = 175 "Unexpected class cast exception while processing an event." 176 + "\nEvent: " + object; 177 throw new IllegalStateException( error ); 178 } 179 } 180 catch( InterruptedException e ) 181 { 182 return; 183 } 184 } 185 186 Object source = event.getSource(); 187 if( source instanceof EventHandler ) 188 { 189 EventHandler handler = (EventHandler) source; 190 try 191 { 192 handler.processEvent( event ); 193 } 194 catch( Throwable e ) 195 { 196 final String error = 197 "Unexpected error while processing event." 198 + "\nEvent: " + event 199 + "\nSource: " + this; 200 getLogger().error( error, e ); 201 } 202 } 203 else 204 { 205 final String error = 206 "Event source is not an instance of " 207 + EventHandler.class.getName(); 208 getLogger().error( error ); 209 } 210 } 211 } 212 } 213 214 /** 215 * Enqueue an event for delivery to registered 216 * listeners unless there are no registered 217 * listeners. 218 * 219 * @param event the event object to add to the queue 220 */ 221 public void enqueueEvent( EventObject event ) 222 { 223 enqueueEvent( event, false ); 224 } 225 226 /** 227 * Enqueue an event for delivery to registered 228 * listeners unless there are no registered 229 * listeners. 230 * 231 * @param event the event object to add to the queue 232 * @param waitForCompletion if TRUE the implementation will apply 233 * the event to the event source event handler and return on 234 * copmpletion of evetn delivery 235 */ 236 public void enqueueEvent( EventObject event, boolean waitForCompletion ) 237 { 238 if( !waitForCompletion ) 239 { 240 synchronized( m_queue ) 241 { 242 m_queue.add( event ); 243 m_queue.notify(); 244 } 245 } 246 else 247 { 248 Object source = event.getSource(); 249 if( source instanceof EventHandler ) 250 { 251 EventHandler handler = (EventHandler) source; 252 try 253 { 254 handler.processEvent( event ); 255 } 256 catch( Throwable e ) 257 { 258 final String error = 259 "Unexpected error while processing event." 260 + "\nEvent: " + event 261 + "\nSource: " + source; 262 getLogger().error( error ); 263 } 264 } 265 else 266 { 267 final String error = 268 "Event source is not an instance of " 269 + EventHandler.class.getName() 270 + "\nSource: " + source.getClass().getName(); 271 throw new IllegalStateException( error ); 272 } 273 } 274 } 275 276 /** 277 * Return a logging channel for the supplied name. 278 * @param name the name to use in construction of the logging channel 279 * @return the logging channel 280 */ 281 static Logger getLoggerForCategory( String name ) 282 { 283 if( null == name ) 284 { 285 return new LoggingAdapter( "" ); 286 } 287 else 288 { 289 return new LoggingAdapter( name ); 290 } 291 } 292 }