FileDocCategorySizeDatePackage
BasicController.javaAPI DocJMF 2.1.1e45117Mon May 12 12:20:48 BST 2003com.sun.media

BasicController

public abstract class BasicController extends Object implements Duration, Controller
Media Controller implements the basic functionalities of a java.media.Controller. These include:<
  • The clock calculations using the BasicClock helper class.
  • The RealizeWorkThread and PrefetchWorkThread to implement realize() and prefetch() in the correct unblocking manner.
  • The ListenerList to maintain the list of ControllerListener.
  • Two ThreadedEventQueues for incoming and outgoing ControllerEvents.

version
1.9, 98/11/19

Fields Summary
private int
targetState
protected int
state
private Vector
listenerList
private SendEventQueue
sendEvtQueue
private ConfigureWorkThread
configureThread
private RealizeWorkThread
realizeThread
private PrefetchWorkThread
prefetchThread
protected String
processError
private Clock
clock
private TimedStartThread
startThread
private StopTimeThread
stopTimeThread
private boolean
interrupted
private Object
interruptSync
static final int
Configuring
static final int
Configured
private static JMFSecurity
jmfSecurity
private static boolean
securityPrivelege
private Method[]
m
private Class[]
cl
private Object[]
args
protected boolean
stopThreadEnabled
static String
TimeBaseError
Set the timebase used by the controller. i.e. all media-time to time-base-time will be computed with the given time base. The subclass should implement and further specialized this method to do the real work. But it should also invoke this method to maintain the correct states.
static String
SyncStartError
Start the controller. Invoke clock.start() to maintain the clock states. It starts a wait thread to wait for the given tbt. At tbt, it will wake up and call doStart(). A subclass should implement the doStart() method to do the real work.
static String
StopTimeError
Set the stop time. Invoke clock.setStopTime() to maintain the clock states. The subclass should implement and further specialized this method to do the real work. But it should also invoke this method to maintain the correct states.
static String
MediaTimeError
Set the media time. Invoke clock.setMediaTime() to maintain the clock states. The subclass should implement and further specialized this method to do the real work. But it should also invoke this method to maintain the correct states.
static String
GetTimeBaseError
Get the current time base.
static String
SetRateError
Set the rate of presentation: 1.0: normal, 2.0: twice the speed. -2.0: twice the speed in reverse. Note that not all rates are supported. Invokes clock.setRate() to maintain the clock states. The subclass SHOULDN'T in general override this class. If necessary, it should override the behavior using the doSetRate method. By overriding the doSetRate method, subclass Conrollers can support negative rates, fractional rates etc., but they should guard against illegal rates from going into the clock calculations.
static String
LatencyError
Returns the start latency. Don't know until the particular node is implemented.
static String
DeallocateError
Release the resouces held by the controller. It obeys strict rules as specified in the spec. Implement the abortRealize and abortPrefetch methods to actually do the work. This is a blocking call. It returns only when the Controller has done deallocating the resources. This should be called from an external thread outside of the controller. Take caution if this is being call from inside the Controller. It may cause deadlock.
Constructors Summary
public BasicController()


     
	try {
	    jmfSecurity = JMFSecurityManager.getJMFSecurity();
	    securityPrivelege = true;
	} catch (SecurityException e) {
	}



    
	if ( /*securityPrivelege &&*/ (jmfSecurity != null) ) {
	    String permission = null;

	    try {
		if (jmfSecurity.getName().startsWith("jmf-security")) {
		    permission = "thread";
		    jmfSecurity.requestPermission(m, cl, args, JMFSecurity.THREAD);
		    m[0].invoke(cl[0], args[0]);
		    
		    permission = "thread group";
		    jmfSecurity.requestPermission(m, cl, args, JMFSecurity.THREAD_GROUP);
		    m[0].invoke(cl[0], args[0]);
		} else if (jmfSecurity.getName().startsWith("internet")) {
		    PolicyEngine.checkPermission(PermissionID.THREAD);
		    PolicyEngine.assertPermission(PermissionID.THREAD);
		}
	    } catch (Throwable e) {
		if (JMFSecurityManager.DEBUG) {
		    System.err.println("Unable to get " + permission +
				       " privilege  " + e);
		}
		securityPrivelege = false;
		// TODO: Do the right thing if permissions cannot be obtained.
		// User should be notified via an event
	    }
	}


	if ( (jmfSecurity != null) && (jmfSecurity.getName().startsWith("jdk12"))) {
	    try {
  		Constructor cons = CreateWorkThreadAction.cons;
 		sendEvtQueue = (SendEventQueue) jdk12.doPrivM.invoke(
                                           jdk12.ac,
 					  new Object[] {
 					  cons.newInstance(
 					   new Object[] {
                                               SendEventQueue.class,
					       BasicController.class,
                                               this
                                           })});

		sendEvtQueue.setName(sendEvtQueue.getName() + ": SendEventQueue: " +
				 this.getClass().getName());
		sendEvtQueue.start();
		clock = new BasicClock();
	    } catch (Exception e) {
	    }
	} else {
	    sendEvtQueue = new SendEventQueue(this);
	    sendEvtQueue.setName(sendEvtQueue.getName() + ": SendEventQueue: " +
				 this.getClass().getName());
	    sendEvtQueue.start();
	    clock = new BasicClock();
	}
    
Methods Summary
protected voidabortConfigure()
Called when the configure() is aborted, i.e. deallocate() was called while realizing. Release all resources claimed previously by the configure() call. Override this to implement subclass behavior.

protected abstract voidabortPrefetch()
Called when the prefetch() is aborted, i.e. deallocate() was called while prefetching. Release all resources claimed previously by the prefetch call. Override this to implement subclass behavior.

protected abstract voidabortRealize()
Called when the realize() is aborted, i.e. deallocate() was called while realizing. Release all resources claimed previously by the realize() call. Override this to implement subclass behavior.

private booleanactivateStopThread(long timeToStop)


	if (getStopTime().getNanoseconds() == Long.MAX_VALUE)
	    return false;

	if (stopTimeThread != null && stopTimeThread.isAlive()) {
	    stopTimeThread.abort();
	    stopTimeThread = null;
	}

	if (timeToStop > 100000000) {
	    if ( /*securityPrivelege &&*/ (jmfSecurity != null) ) {
		    String permission = null;

	        try {
		    if (jmfSecurity.getName().startsWith("jmf-security")) {
			    
			permission = "thread";
			jmfSecurity.requestPermission(m, cl, args, JMFSecurity.THREAD);
			m[0].invoke(cl[0], args[0]);
			    
			permission = "thread group";
			jmfSecurity.requestPermission(m, cl, args, JMFSecurity.THREAD_GROUP);
			m[0].invoke(cl[0], args[0]);
		    } else if (jmfSecurity.getName().startsWith("internet")) {
			PolicyEngine.checkPermission(PermissionID.THREAD);
			PolicyEngine.assertPermission(PermissionID.THREAD);
		    }
	        } catch (Throwable e) {
		    if (JMFSecurityManager.DEBUG) {
			System.err.println("Unable to get " + permission +
					       " privilege  " + e);
		    }
		    securityPrivelege = false;
		    // TODO: Do the right thing if permissions cannot be obtained.
		    // User should be notified via an event
		}
	    }

 	    if ( (jmfSecurity != null) && (jmfSecurity.getName().startsWith("jdk12"))) {
		try {
  		    Constructor cons = CreateTimedThreadAction.cons;
 		    stopTimeThread = (StopTimeThread) jdk12.doPrivM.invoke(
                                           jdk12.ac,
 					  new Object[] {
 					  cons.newInstance(
 					   new Object[] {
                                               StopTimeThread.class,
					       BasicController.class,
                                               this,
					       new Long(timeToStop),
                                           })});


		    stopTimeThread.start();
		} catch (Exception e) { }

 	    } else {
		(stopTimeThread = new StopTimeThread(this, timeToStop)).start();
	    }

	    return false;

	} else {
	    return true;
	}
    
public final voidaddControllerListener(javax.media.ControllerListener listener)
Add a listener to the listenerList. This listener will be notified of this controller's event. This needs to be a synchronized method so as to maintain the integrity of the listenerList.

	if (listenerList == null) {
	    listenerList = new Vector();
	}
	
	synchronized (listenerList) {
	    if (!listenerList.contains(listener)) {
		listenerList.addElement(listener);
	    }
	}
    
private longcheckStopTime()

	long stopTime = getStopTime().getNanoseconds();

	if (stopTime == Long.MAX_VALUE)
	    return 1;

	return (long) ((stopTime - getMediaTime().getNanoseconds()) / getRate());
    
public final voidclose()
A subclass of this implement close to stop all threads to make it "finalizable", i.e., ready to be garbage collected.

	doClose();

	// Interrupt the controller.  If it's in any of the state
	// transition threads, the threads will be interrupted and
	// that transition will be aborted.
	interrupt();

	if (startThread != null)
	    startThread.abort();

	if (stopTimeThread != null)
	    stopTimeThread.abort();

	if (sendEvtQueue != null) {
	    sendEvtQueue.kill();
	    sendEvtQueue = null;
	}
    
protected synchronized voidcompleteConfigure()
Called when the controller is realized and when all the ConfigureCompleteEvents from down stream Controllers have been received. If a subclass wants to override this method, it should still invoke this to ensure the correct events being sent to the upstream Controllers.

	state = Configured;
	sendEvent(new ConfigureCompleteEvent(this, Configuring, Configured, getTargetState()));

	if (getTargetState() >= Realized) {
	    realize();
	}
    
protected voidcompletePrefetch()
Called when the controller is prefetched and when all the PrefetchCompleteEvents from down stream nodes have been received. If a subclass wants to override this method, it should still invoke this to ensure the correct events being sent to the upstream Controllers.

	// Send back the events to whoever is listening, most likely the
	// upstream nodes.
	clock.stop();
	state = Prefetched;
	sendEvent(new PrefetchCompleteEvent(this, Prefetching, Prefetched, getTargetState()));
    
protected synchronized voidcompleteRealize()
Called when the controller is realized and when all the RealizeCompleteEvents from down stream Controllers have been received. If a subclass wants to override this method, it should still invoke this to ensure the correct events being sent to the upstream Controllers.

	// Send back the events to whoever is listening, most likely the
	// upstream nodes.
	state = Realized;
	sendEvent(new RealizeCompleteEvent(this, Realizing, Realized, getTargetState()));

	if (getTargetState() >= Prefetched) {
	    prefetch();
	}
    
public synchronized voidconfigure()

	if (getTargetState() < Configured)
	    setTargetState(Configured);

	switch(state) {
	case Configured:
	case Realizing:
	case Realized:
	case Prefetching:
	case Prefetched:
	case Started:
	    sendEvent(new ConfigureCompleteEvent(this, state, state, getTargetState()));
	    break;
	case Configuring:
	    break;
	case Unrealized:
	    state = Configuring;
	    sendEvent(new TransitionEvent(this, Unrealized, Configuring, getTargetState()));

	    if ( /*securityPrivelege &&*/ (jmfSecurity != null) ) {
		String permission = null;
		try {
		    if (jmfSecurity.getName().startsWith("jmf-security")) {
			permission = "thread";
			jmfSecurity.requestPermission(m, cl, args, JMFSecurity.THREAD);
			m[0].invoke(cl[0], args[0]);
			
			permission = "thread group";
			jmfSecurity.requestPermission(m, cl, args, JMFSecurity.THREAD_GROUP);
			m[0].invoke(cl[0], args[0]);
		    } else if (jmfSecurity.getName().startsWith("internet")) {
			PolicyEngine.checkPermission(PermissionID.THREAD);
			PolicyEngine.assertPermission(PermissionID.THREAD);
		    }
		} catch (Throwable e) {
		    if (JMFSecurityManager.DEBUG) {
			System.err.println("Unable to get " + permission +
					   " privilege  " + e);
		    }
		    securityPrivelege = false;
		    // TODO: Do the right thing if permissions cannot be obtained.
		    // User should be notified via an event
		}
	    }

 	    if ( (jmfSecurity != null) && (jmfSecurity.getName().startsWith("jdk12"))) {
	    try {
		Constructor cons = CreateWorkThreadAction.cons;
 		configureThread = (ConfigureWorkThread) jdk12.doPrivM.invoke(
                                           jdk12.ac,
 					  new Object[] {
 					  cons.newInstance(
 					   new Object[] {
                                               ConfigureWorkThread.class,
					       BasicController.class,
                                               this
                                           })});

		configureThread.setName(configureThread.getName() +
					"[ " + this + " ]" +
					" ( configureThread)");
		
		configureThread.start();
	    } catch (Exception e) {
	    }

 	    } else {
		configureThread = new ConfigureWorkThread(this);
		configureThread.setName(configureThread.getName() +
					"[ " + this + " ]" +
					" ( configureThread)");
		
		configureThread.start();
 	    }
	}
    
public final voiddeallocate()

        

	int previousState = getState();
	// It's illegal to use deallocate on a started controller.
	if (state == Started) {
	    throwError(new ClockStartedError(DeallocateError));
        }

	// stop the thread even if isAlive() is false as
        // we want to kill the thread that has been created
        // but not scheduled to run

	switch (state) {
	case Configuring:
	case Realizing:
	    interrupt();
	    state = Unrealized;
  	    break;
	case Prefetching:
	    interrupt();
	    state = Realized;
	    break;
	case Prefetched:
	    abortPrefetch();
	    state = Realized;
	    resetInterrupt();
	    break;
	}

	setTargetState(state);

	// Use by subclass to add in its own behavior.
	doDeallocate();

	// Wait for the interrupt to take effect.
	synchronized (interruptSync) {
	    while (isInterrupted()) {
		try {
		    interruptSync.wait();
		} catch (InterruptedException e) {}
	    }
	}

	sendEvent(new DeallocateEvent(this, previousState, state,
				      state, getMediaTime()));
    
protected final voiddispatchEvent(javax.media.ControllerEvent evt)
An internal function to notify the listeners on the listener list the given event. This gets called by the sendEvtQueue's processEvent() callback. This method updates a lock on the Vector listenerList before enumerating it.


	if (listenerList == null)
	    return;

	synchronized(listenerList) {
	    Enumeration list = listenerList.elements();
	    while (list.hasMoreElements()) {
		ControllerListener listener = (ControllerListener)list.nextElement();
		listener.controllerUpdate(evt);
	    }
	}
    
protected voiddoClose()
Invoked by close() to cleanup the Controller. Override this to implement subclass behavior.

    
protected booleandoConfigure()
The stub function to perform the steps to configure the controller. Call configure() for the public method.

return true;
protected voiddoDeallocate()
Called by deallocate(). Subclasses should implement this for its specific behavior.

    
protected voiddoFailedConfigure()
Called when realize() has failed.

	state = Unrealized;
	setTargetState(Unrealized);
	String msg = "Failed to configure";
	if (processError != null)
	    msg = msg + ": " + processError;
	sendEvent(new ResourceUnavailableEvent(this, msg));
	processError = null;
    
protected voiddoFailedPrefetch()
Called when the prefetch() has failed.

	state = Realized;
	setTargetState(Realized);
	String msg = "Failed to prefetch";
	if (processError != null)
	    msg = msg + ": " + processError;
	sendEvent(new ResourceUnavailableEvent(this, msg));
	processError = null;
    
protected voiddoFailedRealize()
Called when realize() has failed.

	state = Unrealized;
	setTargetState(Unrealized);
	String msg = "Failed to realize";
	if (processError != null)
	    msg = msg + ": " + processError;
	sendEvent(new ResourceUnavailableEvent(this, msg));
	processError = null;
    
protected abstract booleandoPrefetch()
The stub function to perform the steps to prefetch the controller. Call prefetch for the public method. This is called from a separately running thread. So do take necessary precautions to protect shared resources. It's OK to put an empty stub function body here.

Return true if the prefetch is successful. Return false and set the processError string if failed.

This function is not declared synchronized because first it is already guaranteed by realize() not to be called more than once simultaneously. Secondly if this is synchronized, then other synchronized methods, deallocate() and processEvent() will be blocked since they are synchronized methods. Override this to implement subclass behavior.

return
true if successful.

protected abstract booleandoRealize()
The stub function to perform the steps to realize the controller. Call realize() for the public method. This is called from a separately running thread. So do take necessary precautions to protect shared resources. It's OK to put an empty stub function body here.

Return true if the realize is successful. Return false and set the processError string if failed.

This function is not declared synchronized because first it is already guaranteed by realize() not to be called more than once simultaneously. Secondly if this is synchronized, then other synchronized methods, deallocate() and processEvent() will be blocked since they are synchronized methods. Override this to implement subclass behavior.

return
true if successful.

protected voiddoSetMediaTime(javax.media.Time when)

    
protected floatdoSetRate(float factor)

	return factor;
    
protected abstract voiddoStart()
Start immediately. Invoked from start(tbt) when the scheduled start time is reached. Use the public start(tbt) method for the public interface. Override this to implement subclass behavior.

protected voiddoStop()
Invoked from stop(). Override this to implement subclass behavior.

protected javax.media.ClockgetClock()

	return clock;
    
public javax.media.ControlgetControl(java.lang.String type)
Get the Control that supports the class or interface specified. The full class or interface name should be specified. Null is returned if the Control is not supported.

return
Control for the class or interface name.

	Class cls;
	try {
	    cls = Class.forName(type);
	} catch (ClassNotFoundException e) {
	    return null;
	}
	Control cs[] = getControls();
	for (int i = 0; i < cs.length; i++) {
	    if (cls.isInstance(cs[i]))
		return cs[i];
	}
	return null;
    
public javax.media.Control[]getControls()
Return a list of Control objects this Controller supports. If there are no controls, then an array of length zero is returned.

return
list of Controller controls.

      // Not implemented $$$
      // Is this correct ? $$$
      return new Control[0];
    
public javax.media.TimegetDuration()
Return the duration of the media. It's unknown until we implement a particular node.

return
the duration of the media.

	return Duration.DURATION_UNKNOWN;
    
public longgetMediaNanoseconds()
Get the current media time in nanoseconds.

return
the media time in nanoseconds.

	return clock.getMediaNanoseconds();
    
public javax.media.TimegetMediaTime()
Return the current media time. Uses the clock to do the computation. A subclass can override this method to do the right thing for itself.

return
the current media time.

	return clock.getMediaTime();
    
public floatgetRate()
Get the current presentation speed.

return
the current presentation speed.

	return clock.getRate();
    
public javax.media.TimegetStartLatency()

       
	if (state < Realized) {
	    throwError(new NotRealizedError(LatencyError));
	}
	return LATENCY_UNKNOWN;
    
public final intgetState()
Get the current state of the controller.

return
the current state of the controller.

	return state;
    
public javax.media.TimegetStopTime()
Get the preset stop time.

return
the preset stop time.

	return clock.getStopTime();
    
public javax.media.TimegetSyncTime()
Return the Sync Time. Not yet implementated.

	return new Time(0);
    
public final intgetTargetState()
Get the current target state.

return
the current target state.

	return targetState;
    
public javax.media.TimeBasegetTimeBase()

       
	if (state < Realized) {
	    throwError(new NotRealizedError(GetTimeBaseError));
	}

	return clock.getTimeBase();
    
protected voidinterrupt()
Interrupt the process.

	synchronized (interruptSync) {
	    interrupted = true;
	    interruptSync.notify();
	}
    
protected abstract booleanisConfigurable()
Subclass should define this. If this return true, the controller will go through the Configured state. For example, the Controller implementing the Processor should return true.

protected booleanisInterrupted()
Return true if the process is interrupted.

	return interrupted;
    
public javax.media.TimemapToTimeBase(javax.media.Time t)
Map the given media-time to time-base-time.

param
t given media time.
return
timebase time.
exception
ClockStoppedException thrown if the Controller has already been stopped.

	return clock.mapToTimeBase(t);
    
public final voidprefetch()
Take the necessary steps to prefetch the controller. This is a non-blocking call. It starts a work thread to do the real work. The actual code to do the realizing should be written in doPrefetch(). The thread is also responsible for catching all the PrefetchCompleteEvents from the down stream nodes. When the steps to prefetch the controller are completed and when all the PrefetchCompleteEvents from down stream nodes have been received, the completePrefetch() call will be invoked.

	if (getTargetState() <= Realized)
	    setTargetState(Prefetched);
	switch (state) {
	case Prefetched:
	case Started:
	    sendEvent(new PrefetchCompleteEvent(this, state, state, getTargetState()));
	    break;
	case Configuring:
	case Realizing:
	case Prefetching:
	    break; // $$ Nothing is done. 
	case Unrealized:
	case Configured:
	    // The controller is not realized yet, we have to implicitly
	    // carry out a realize().
	    realize();
	    break;
	case Realized:
	    // Put it in the prefetching state.
	    //	synchronized(this) {
	    state = Prefetching;
	    sendEvent(new TransitionEvent(this, Realized, Prefetching, getTargetState()));

	    // Start the prefetch thread for this controller.
	    if ( /*securityPrivelege && */ (jmfSecurity != null) ) {
		String permission = null;
		try {
		    if (jmfSecurity.getName().startsWith("jmf-security")) {
			permission = "thread";
			jmfSecurity.requestPermission(m, cl, args, JMFSecurity.THREAD);
			m[0].invoke(cl[0], args[0]);
			
			permission = "thread group";
			jmfSecurity.requestPermission(m, cl, args, JMFSecurity.THREAD_GROUP);
			m[0].invoke(cl[0], args[0]);
		    } else if (jmfSecurity.getName().startsWith("internet")) {
			PolicyEngine.checkPermission(PermissionID.THREAD);
			PolicyEngine.assertPermission(PermissionID.THREAD);
		    }
		} catch (Throwable e) {
		    if (JMFSecurityManager.DEBUG) {
			System.err.println("Unable to get " + permission +
					   " privilege  " + e);
		    }
		    securityPrivelege = false;
		    // TODO: Do the right thing if permissions cannot be obtained.
		    // User should be notified via an event
		}
	    }
 	    if ( (jmfSecurity != null) && (jmfSecurity.getName().startsWith("jdk12"))) {
	    try {
  		Constructor cons = CreateWorkThreadAction.cons;
 		prefetchThread = (PrefetchWorkThread) jdk12.doPrivM.invoke(
                                           jdk12.ac,
 					  new Object[] {
 					  cons.newInstance(
 					   new Object[] {
                                               PrefetchWorkThread.class,
					       BasicController.class,
                                               this
                                           })});

		prefetchThread.setName(prefetchThread.getName() +
				      "[ " + this + " ]" +
				      " ( prefetchThread)");
		
		prefetchThread.start();

	    } catch (Exception e) {
	    }
	} else {
		prefetchThread = new PrefetchWorkThread(this);
		prefetchThread.setName(prefetchThread.getName() + " ( prefetchThread)");
		prefetchThread.start();
 	    }
	    //	}
	    break;
	}
    
public final synchronized voidrealize()
Take the necessary steps to realize the controller. This is a non-blocking call. It starts a work thread to do the real work. The actual code to do the realizing should be written in doRealize(). The thread is also responsible for catching all the RealizeCompleteEvents from the down stream nodes. When the steps to realize the controller are completed and when all the RealizeCompleteEvents from down stream nodes have been received, the completeRealize() call will be invoked.


	if (getTargetState() < Realized)
	    setTargetState(Realized);

	switch (state) {
	case Realized:
	case Prefetching:
	case Prefetched:
	case Started:
	    sendEvent(new RealizeCompleteEvent(this, state, state, getTargetState()));
	    break;
	case Realizing:
	case Configuring:
	    break; // $$ Nothing is done. Two realize() will result in one event
	case Unrealized:
	    // For processors, we'll implicitly call configure.
	    if (isConfigurable()) {
		configure();
		break;
	    }
	case Configured:
	    // Put it in the realizing state.
	    int oldState = state;
	    state = Realizing;
	    sendEvent(new TransitionEvent(this, oldState, Realizing, getTargetState()));

	    // Start the realize thread for this controller.

	    if ( /*securityPrivelege &&*/ (jmfSecurity != null) ) {
		String permission = null;
		try {
		    if (jmfSecurity.getName().startsWith("jmf-security")) {
			permission = "thread";
			jmfSecurity.requestPermission(m, cl, args, JMFSecurity.THREAD);
			m[0].invoke(cl[0], args[0]);
			
			permission = "thread group";
			jmfSecurity.requestPermission(m, cl, args, JMFSecurity.THREAD_GROUP);
			m[0].invoke(cl[0], args[0]);
		    } else if (jmfSecurity.getName().startsWith("internet")) {
			PolicyEngine.checkPermission(PermissionID.THREAD);
			PolicyEngine.assertPermission(PermissionID.THREAD);
		    }
		} catch (Throwable e) {
		    if (JMFSecurityManager.DEBUG) {
			System.err.println("Unable to get " + permission +
					   " privilege  " + e);
		    }
		    securityPrivelege = false;
		    // TODO: Do the right thing if permissions cannot be obtained.
		    // User should be notified via an event
		}
	    }
 	    if ( (jmfSecurity != null) && (jmfSecurity.getName().startsWith("jdk12"))) {
	    try {
  		Constructor cons = CreateWorkThreadAction.cons;
 		realizeThread = (RealizeWorkThread) jdk12.doPrivM.invoke(
                                           jdk12.ac,
 					  new Object[] {
 					  cons.newInstance(
 					   new Object[] {
                                               RealizeWorkThread.class,
					       BasicController.class,
                                               this
                                           })});

		realizeThread.setName(realizeThread.getName() +
				      "[ " + this + " ]" +
				      " ( realizeThread)");
		
		realizeThread.start();

	    } catch (Exception e) {
	    }
	} else {
		realizeThread = new RealizeWorkThread(this);
		realizeThread.setName(realizeThread.getName() +
				      "[ " + this + " ]" +
				      " ( realizeThread)");
		
		realizeThread.start();
 	    }
	    break;
	}
    
public final voidremoveControllerListener(javax.media.ControllerListener listener)
Remove a listener from the listener list. The listener will stop receiving notification from this controller. This needs to be a synchronized method so as to maintain the integrity of the listenerList.

	if (listenerList == null)
	    return;
	synchronized (listenerList) {
	    if (listenerList != null) {
		listenerList.removeElement(listener);
	    }
	}
    
protected voidresetInterrupt()
Reset the interrupted flag.

	synchronized (interruptSync) {
	    interrupted = false;
	    interruptSync.notify();
	}
    
protected final voidsendEvent(javax.media.ControllerEvent evt)
Send an event to the listeners listening to my events. The event is Queued in the sendEvtQueue which runs in a separate thread. This way, sendEvent() won't be blocked.

	if (sendEvtQueue != null)
	    sendEvtQueue.postEvent(evt);
    
protected voidsetClock(javax.media.Clock c)
Subclass can use this to switch to a different default clock.

	clock = c;
    
protected voidsetMediaLength(long t)

	if (clock instanceof BasicClock)
	    ((BasicClock)clock).setMediaLength(t);
    
public voidsetMediaTime(javax.media.Time when)

        
	if (state < Realized) {
	    throwError(new NotRealizedError(MediaTimeError));
	}
	clock.setMediaTime(when);
	doSetMediaTime(when);
	sendEvent(new MediaTimeSetEvent(this, when));
    
public floatsetRate(float factor)

        
	if (state < Realized) {
	    throwError(new NotRealizedError(SetRateError));
	}

	float oldRate = getRate();
	float rateSet = doSetRate(factor);
	float newRate =  clock.setRate(rateSet);

	if (newRate != oldRate) {
	    sendEvent(new RateChangeEvent(this, newRate));
	}
	return newRate;
    
public voidsetStopTime(javax.media.Time t)

        

	if (state < Realized) {
	    throwError(new NotRealizedError(StopTimeError));
	}
	Time oldStopTime = getStopTime();
	clock.setStopTime(t);
	boolean stopTimeHasPassed = false;
	if (state == Started) {
	    long timeToStop;
	    if (((timeToStop = checkStopTime()) < 0) || 
		(stopThreadEnabled && activateStopThread(timeToStop)))
		stopTimeHasPassed = true;
	}
	if ( oldStopTime.getNanoseconds() != t.getNanoseconds() ) {
	    sendEvent(new StopTimeChangeEvent(this, t));
	}
	if (stopTimeHasPassed) {
	    stopAtTime();
	}
    
protected final voidsetTargetState(int state)
Set the target state.

	targetState = state;
    
public voidsetTimeBase(javax.media.TimeBase tb)

          
	if (state < Realized) {
	    throwError(new NotRealizedError(TimeBaseError));
	}
	clock.setTimeBase(tb);
    
public voidstop()
Stop the controller. Invoke clock.stop() to maintain the clock states. The subclass should implement and further specialized this method to do the real work. But it should also invoke this method to maintain the correct states.

	if (state == Started || state == Prefetching) {
	    stopControllerOnly();
	    doStop();
	}
    
protected voidstopAtTime()
Stop because stop time has been reached. Subclasses should override this method.

	stop();
	setStopTime(Clock.RESET);
	sendEvent(new StopAtTimeEvent(this, Started,
				       Prefetched,
				       getTargetState(),
				       getMediaTime()));
    
protected voidstopControllerOnly()
Stop the controller. Invoke clock.stop() to maintain the clock states. The subclass should implement and further specialized this method to do the real work. But it should also invoke this method to maintain the correct states.

	if (state == Started || state == Prefetching) {
	    clock.stop();
	    state = Prefetched;
	    setTargetState(Prefetched);

	    if ( stopTimeThread != null && stopTimeThread.isAlive() &&
		 Thread.currentThread() != stopTimeThread ) {
		stopTimeThread.abort();
	    }

	    // If start(tbt) was invoked and it hasn't reached the
	    // scheduled tbt yet, the startThread is spinning and
	    // we need to abort that.
	    if (startThread != null && startThread.isAlive())
		startThread.abort();
	}
    
public voidsyncStart(javax.media.Time tbt)

         
	if (state < Prefetched) {
	    throwError(new NotPrefetchedError(SyncStartError));
	}
	clock.syncStart(tbt); // Will generate ClockStartedError if state is Started
	state = Started;
	setTargetState(Started);
	sendEvent(new StartEvent(this, Prefetched, Started,
				 Started, getMediaTime(), tbt));
	long timeToStop;
	if ((timeToStop = checkStopTime()) < 0 || 
	    (stopThreadEnabled && activateStopThread(timeToStop))) {
	    // If the stop-time is set to a value that the Clock
	    // has already passed, the Clock immediately stops.
	    stopAtTime();
	    return;
	}

	// Schedule the start time.
	// startThread will wake up at the scheduled tbt and call the
	// protected doStart() method.

	if ( /*securityPrivelege && */ (jmfSecurity != null) ) {
	    String permission = null;
	    try {
		if (jmfSecurity.getName().startsWith("jmf-security")) {
		    permission = "thread";
		    jmfSecurity.requestPermission(m, cl, args, JMFSecurity.THREAD);
		    m[0].invoke(cl[0], args[0]);
		    
		    permission = "thread group";
		    jmfSecurity.requestPermission(m, cl, args, JMFSecurity.THREAD_GROUP);
		    m[0].invoke(cl[0], args[0]);
		} else if (jmfSecurity.getName().startsWith("internet")) {
		    PolicyEngine.checkPermission(PermissionID.THREAD);
		    PolicyEngine.assertPermission(PermissionID.THREAD);
		}
	    } catch (Throwable e) {
		if (JMFSecurityManager.DEBUG) {
		    System.err.println("Unable to get " + permission +
				       " privilege  " + e);
		}
		securityPrivelege = false;
		// TODO: Do the right thing if permissions cannot be obtained.
		// User should be notified via an event
	    }
	}

 	if ( (jmfSecurity != null) && (jmfSecurity.getName().startsWith("jdk12"))) {
	    try {
  		Constructor cons = CreateTimedThreadAction.cons;
 		startThread = (TimedStartThread) jdk12.doPrivM.invoke(
                                           jdk12.ac,
 					  new Object[] {
 					  cons.newInstance(
 					   new Object[] {
                                               TimedStartThread.class,
					       BasicController.class,
                                               this,
					       new Long(tbt.getNanoseconds()),
                                           })});


	    startThread.setName(startThread.getName() + " ( startThread: " + this + " )");
	    startThread.start();
	    } catch (Exception e) {
	    }

 	} else {
	    startThread = new TimedStartThread(this, tbt.getNanoseconds());
	    startThread.setName(startThread.getName() + " ( startThread: " + this + " )");
	    startThread.start();
	}
    
protected booleansyncStartInProgress()

	return (startThread != null && startThread.isAlive());
    
protected voidthrowError(java.lang.Error e)

	Log.dumpStack(e);
	throw e;