/* * This file is modified by Ivan Maidanski <ivmai@ivmaisoft.com> * Project name: JCGO-SUNAWT (http://www.ivmaisoft.com/jcgo/) */ /* * @(#)EventDispatcher.java 1.27 03/01/23 * * Copyright 2003 Sun Microsystems, Inc. All rights reserved. * SUN PROPRIETARY/CONFIDENTIAL. Use is subject to license terms. */ package com.sun.media.sound; import java.util.EventObject; import java.util.Vector; import javax.sound.sampled.Clip; import javax.sound.sampled.Line; import javax.sound.sampled.LineEvent; import javax.sound.sampled.LineListener; import javax.sound.midi.MetaMessage; import javax.sound.midi.ShortMessage; import javax.sound.midi.MetaEventListener; import javax.sound.midi.ControllerEventListener; import java.lang.reflect.Method; import java.lang.reflect.Constructor; import java.lang.reflect.InvocationTargetException; // needs at least J2SE 1.2.x! import java.util.ArrayList; /** * EventDispatcher. Used by various classes in the Java Sound implementation * to send events. * * @version 1.27 03/01/23 * @author David Rivas * @author Kara Kytle * @author Florian Bomers */ class EventDispatcher implements Runnable { /** * time of inactivity until the auto closing clips * are closed */ private static final int AUTO_CLOSE_TIME = 5000; /** * the last time that an event was processed */ private long lastProcessEventTime = System.currentTimeMillis(); /** * List of events */ private Vector eventQueue = new Vector(); /** * True if thread should exit */ private boolean done = false; /** * Thread object for this EventDispatcher instance */ Thread thread = null; static boolean creatingThread = false; // Variables needed for Security private static JSSecurity jsSecurity = null; private static boolean securityPrivilege = false; private static ThreadGroup topmostThreadGroup = null; /* private Method m[] = new Method[1]; private Class cl[] = new Class[1]; private Object args[][] = new Object[1][0]; */ /* * support for auto-closing Clips */ private ArrayList autoClosingClips = new ArrayList(); /** * Static block for getting security permissions */ // $$fb 2001-11-01 part of fix for Bug 4521048: Applets: Java Sound dies with an exception at init time // do not use cached instance of JSSecurity private static void initSecurity() { securityPrivilege = true; /* if(Printer.debug)Printer.debug("EventDispatcher.initSecurity(): securityPrivilege = " + securityPrivilege); try { jsSecurity = JSSecurityManager.getJSSecurity(); } catch (SecurityException e) { if(Printer.debug)Printer.debug("Exception caught: " + e); if(Printer.debug)Printer.debug("Setting securityPrivilege to false"); securityPrivilege = false; } */ //$$fb copied from MixerThread.java for bug 4521048. Using the main threadgroup also fixes 4304996. if (securityPrivilege) { if(Printer.debug)Printer.debug("EventDispatcher.java: getting the real topmost thread group"); /* if( (jsSecurity!=null) && (jsSecurity instanceof DisabledSecurity) ) { // do nothing } else if( (jsSecurity!=null) && (jsSecurity.getName().startsWith("JDK12") )) { try { // invoke the privileged action using 1.2 security Constructor cons = JDK12TopmostThreadGroupAction.cons; topmostThreadGroup = (ThreadGroup) JDK12.doPrivM.invoke( JDK12.ac, new Object[] { cons.newInstance( new Object[0] ) }); if(Printer.debug)Printer.debug("Got topmost thread group with 1.2 style security"); } catch (Exception e) { if(Printer.debug)Printer.debug("Exception getting topmost thread group with 1.2 style security"); // try without using 1.2 style topmostThreadGroup = getTopmostThreadGroup(); } } else */ { // not JDK 1.2 style, assume we already have permission topmostThreadGroup = getTopmostThreadGroup(); } } /* else { if(Printer.debug)Printer.debug("EventDispatcher.java: no securityPrivilege, settling for current threadgroup"); topmostThreadGroup = Thread.currentThread().getThreadGroup(); } */ } /** * This start() method starts an event thread if one is not already active. */ void start() { if( ((thread == null) && !creatingThread ) || (done) ) { // $$fb 2001-11-01 part of fix for Bug 4521048 initSecurity(); final Runnable localRunnable = this; done = false; /* if( securityPrivilege && (jsSecurity != null) ) { if( jsSecurity.getName().startsWith("JDK12") ) { try{ creatingThread = true; Constructor cons = JDK12NewEventDispatcherAction.cons; //$$fb create thread in main thread group Object args[] = new Object[] { topmostThreadGroup, localRunnable}; thread = (Thread) JDK12.doPrivM.invoke( JDK12.ac, new Object[] { cons.newInstance( args ) }); if(Printer.debug) Printer.debug("Got EventDispatcher with 1.2 style security"); creatingThread = false; } catch( InstantiationException ise) { if( Printer.debug ) Printer.debug("InstantiationException getting event dispatcher with 1.2 style security: "+ ise); // try without using 1.2 style thread = new Thread(topmostThreadGroup, (Runnable)this); thread.start(); } catch( IllegalAccessException iae) { if( Printer.debug ) Printer.debug("IllegalAccessException getting event dispatcher with 1.2 style security: "+iae); // try without using 1.2 style thread = new Thread(topmostThreadGroup, (Runnable)this); thread.start(); } catch( InvocationTargetException ite) { if( Printer.debug ) Printer.debug("InvocationTargetException getting event dispatcher with 1.2 style security: "+ite); // try without using 1.2 style thread = new Thread(topmostThreadGroup, (Runnable)this); thread.start(); } } else { try { jsSecurity.requestPermission(m, cl, args, JSSecurity.THREAD); m[0].invoke(cl[0], args[0]); if(Printer.debug)Printer.debug("EventDispatcher.start(): got THREAD permission"); } catch( InvocationTargetException ite) { if(Printer.debug)Printer.debug("EventDispatcher.start(): could not got THREAD permission"); } catch( IllegalAccessException iae) { if(Printer.debug)Printer.debug("EventDispatcher.start(): could not got THREAD permission"); } thread = new Thread(topmostThreadGroup, this); thread.start(); } } else */ { if(Printer.debug)Printer.debug("EventDispatcher.start(): no securityPrivilege or jsSecurity==null, not using security"); thread = new Thread(topmostThreadGroup, this); thread.start(); } if (thread!=null) { try { thread.setName("Java Sound event dispatcher"); } catch (SecurityException se) {} if (Printer.debug) Printer.debug("Created thread in group "+thread.getThreadGroup()); } } } /** * Invoked when there is at least one event in the queue. * Implement this as a callback to process one event. */ protected void processEvent(EventInfo eventInfo) { // process an LineEvent if (eventInfo.getEvent() instanceof LineEvent) { LineEvent event = (LineEvent)eventInfo.getEvent(); Vector currentListeners = eventInfo.getListeners(); if (Printer.debug) Printer.debug("Sending "+event+" to "+currentListeners.size()+" listeners"); for (int i = 0; i < currentListeners.size(); i++) { ((LineListener)currentListeners.elementAt(i)).update(event); } return; } // process a MetaMessage if (eventInfo.getEvent() instanceof MetaMessage) { MetaMessage event = (MetaMessage)eventInfo.getEvent(); Vector currentListeners = eventInfo.getListeners(); for (int i = 0; i < currentListeners.size(); i++) { ((MetaEventListener)currentListeners.elementAt(i)).meta(event); } return; } // process a Controller or Mode Event if (eventInfo.getEvent() instanceof ShortMessage) { ShortMessage event = (ShortMessage)eventInfo.getEvent(); int status = event.getStatus(); // Controller and Mode events have status byte 0xBc, where // c is the channel they are sent on. if( (status>>4)==11 ) { Vector currentListeners = eventInfo.getListeners(); for (int i = 0; i < currentListeners.size(); i++) { ((ControllerEventListener)currentListeners.elementAt(i)).controlChange(event); } } return; } /* // process a MidiDeviceEvent if (eventInfo.getEvent() instanceof MidiDeviceEvent) { MidiDeviceEvent event = (MidiDeviceEvent)eventInfo.getEvent(); Vector currentListeners = eventInfo.getListeners(); for (int i = 0; i < currentListeners.size(); i++) { ((MidiDeviceListener)currentListeners.elementAt(i)).update(event); } return; } */ Printer.err("Unknown event type: " + eventInfo.getEvent()); } /** * Wait until there is something in the event queue to process. Then * dispatch the event to the listeners.The entire method does not * need to be synchronized since this includes taking the event out * from the queue and processing the event. We only need to provide * exclusive access over the code where an event is removed from the *queue. */ protected void dispatchEvents() { EventInfo eventInfo = null; synchronized (this) { // Wait till there is an event in the event queue. try { if ((!done) && (eventQueue.size() == 0)) { if (autoClosingClips.size() > 0) { // make sure this exceeds AUTO_CLOSE_TIME ! wait(AUTO_CLOSE_TIME + 100); } else { wait(); } } } catch (InterruptedException e) { done = true; notifyAll(); if (Printer.debug) e.printStackTrace(); } if (!done && eventQueue.size() > 0) { // Remove the event from the queue and dispatch it to the listeners. eventInfo = (EventInfo)eventQueue.elementAt(0); eventQueue.removeElementAt(0); } } // end of synchronized if (!done) { if (eventInfo != null) { lastProcessEventTime = System.currentTimeMillis(); processEvent(eventInfo); } else { long timeSinceLastEvent = System.currentTimeMillis() - lastProcessEventTime; if (timeSinceLastEvent >= AUTO_CLOSE_TIME) { closeAutoClosingClips(); } } } } /** * Queue the given event in the event queue. */ synchronized void postEvent(EventInfo eventInfo) { eventQueue.addElement(eventInfo); notifyAll(); } /** * Stop the thread. * $$kk: 12.21.98: we never call this -- but we should!! */ synchronized void kill() { done = true; notifyAll(); } /** * A loop to dispatch events. */ public void run() { while (!done) { try { dispatchEvents(); } catch (Throwable t) { if (Printer.err) t.printStackTrace(); } } if (Printer.debug) Printer.debug("Exiting Java Sound EventDispatcher thread."); } /** * Send audio events. */ void sendAudioEvents(Object event, Vector listeners) { start(); Vector currentListeners = (Vector)listeners.clone(); EventInfo eventInfo = new EventInfo(event, currentListeners); postEvent(eventInfo); } // $$fb 2001-11-01 part of fix for Bug 4521048: Applets: Java Sound dies with an exception at init time // copied from MixerThread.java private static ThreadGroup getTopmostThreadGroup() { if(Printer.trace)Printer.trace(">> EventDispatcher: getTopmostThreadGroup()"); ThreadGroup g = Thread.currentThread().getThreadGroup(); while ((g.getParent() != null) && (g.getParent().getParent() != null)) { g = g.getParent(); } if(Printer.trace)Printer.trace("<< EventDispatcher: getTopmostThreadGroup() completed"); return g; } /* * go through the list of registered auto-closing * Clip instances and close them, if appropriate * * This method is called in regular intervals */ private void closeAutoClosingClips() { synchronized(autoClosingClips) { if (Printer.debug)Printer.debug("> EventDispatcher.closeAutoClosingClips ("+autoClosingClips.size()+" clips)"); for (int i = autoClosingClips.size()-1; i >= 0 ; i--) { AutoClosingClip clip = (AutoClosingClip) autoClosingClips.get(i); // sanity check if (!clip.isOpen() || !clip.isAutoClosing()) { autoClosingClips.remove(i); } else if (!clip.isRunning() && !clip.isActive() && clip.isAutoClosing()) { if (Printer.debug)Printer.debug("EventDispatcher: closing clip "+clip); clip.close(); } else { if (Printer.debug)Printer.debug("Doing nothing with clip "+clip+":"); if (Printer.debug)Printer.debug(" open="+clip.isOpen()+", autoclosing="+clip.isAutoClosing()); if (Printer.debug)Printer.debug(" isRunning="+clip.isRunning()+", isActive="+clip.isActive()); } } } if (Printer.debug)Printer.debug("< EventDispatcher.closeAutoClosingClips ("+autoClosingClips.size()+" clips)"); } /** * called from auto-closing clips when one of their open() method is called */ void autoClosingClipOpened(AutoClosingClip clip) { int index = 0; synchronized(autoClosingClips) { index = autoClosingClips.indexOf(clip); if (index == -1) { if (Printer.debug)Printer.debug("EventDispatcher: adding auto-closing clip "+clip); autoClosingClips.add(clip); } } if (index == -1) { synchronized (this) { // this is only for the case that the first clip is set to autoclosing, // and it is already open, and nothing is done with it. // EventDispatcher.process() method would block in wait() and // never close this first clip, keeping the device open. notifyAll(); } } } /** * called from auto-closing clips when their closed() method is called */ void autoClosingClipClosed(AutoClosingClip clip) { /*synchronized(autoClosingClips) { int index = autoClosingClips.indexOf(clip); if (index >= 0) { if (Printer.debug)Printer.debug("EventDispatcher: removing auto-closing clip "+clip); autoClosingClips.remove(index); } }*/ } /** * Container for an event and a set of listeners to deliver it to. */ class EventInfo { private Object event; private Vector listeners; EventInfo(Object event, Vector listeners) { this.event = event; this.listeners = listeners; } Object getEvent() { return event; } Vector getListeners() { return listeners; } } // class EventInfo } // class EventDisapatcher