Methods Summary |
---|
protected void | abortConfigure()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 void | abortPrefetch()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 void | abortRealize()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 boolean | activateStopThread(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 void | addControllerListener(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 long | checkStopTime()
long stopTime = getStopTime().getNanoseconds();
if (stopTime == Long.MAX_VALUE)
return 1;
return (long) ((stopTime - getMediaTime().getNanoseconds()) / getRate());
|
public final void | close()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 void | completeConfigure()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 void | completePrefetch()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 void | completeRealize()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 void | configure()
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 void | deallocate()
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 void | dispatchEvent(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 void | doClose()Invoked by close() to cleanup the Controller.
Override this to implement subclass behavior.
|
protected boolean | doConfigure()The stub function to perform the steps to configure the controller.
Call configure() for the public method.return true;
|
protected void | doDeallocate()Called by deallocate().
Subclasses should implement this for its specific behavior.
|
protected void | doFailedConfigure()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 void | doFailedPrefetch()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 void | doFailedRealize()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 boolean | doPrefetch()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.
|
protected abstract boolean | doRealize()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.
|
protected void | doSetMediaTime(javax.media.Time when)
|
protected float | doSetRate(float factor)
return factor;
|
protected abstract void | doStart()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 void | doStop()Invoked from stop().
Override this to implement subclass behavior.
|
protected javax.media.Clock | getClock()
return clock;
|
public javax.media.Control | getControl(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.
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.
// Not implemented $$$
// Is this correct ? $$$
return new Control[0];
|
public javax.media.Time | getDuration()Return the duration of the media.
It's unknown until we implement a particular node.
return Duration.DURATION_UNKNOWN;
|
public long | getMediaNanoseconds()Get the current media time in nanoseconds.
return clock.getMediaNanoseconds();
|
public javax.media.Time | getMediaTime()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 clock.getMediaTime();
|
public float | getRate()Get the current presentation speed.
return clock.getRate();
|
public javax.media.Time | getStartLatency()
if (state < Realized) {
throwError(new NotRealizedError(LatencyError));
}
return LATENCY_UNKNOWN;
|
public final int | getState()Get the current state of the controller.
return state;
|
public javax.media.Time | getStopTime()Get the preset stop time.
return clock.getStopTime();
|
public javax.media.Time | getSyncTime()Return the Sync Time.
Not yet implementated.
return new Time(0);
|
public final int | getTargetState()Get the current target state.
return targetState;
|
public javax.media.TimeBase | getTimeBase()
if (state < Realized) {
throwError(new NotRealizedError(GetTimeBaseError));
}
return clock.getTimeBase();
|
protected void | interrupt()Interrupt the process.
synchronized (interruptSync) {
interrupted = true;
interruptSync.notify();
}
|
protected abstract boolean | isConfigurable()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 boolean | isInterrupted()Return true if the process is interrupted.
return interrupted;
|
public javax.media.Time | mapToTimeBase(javax.media.Time t)Map the given media-time to time-base-time.
return clock.mapToTimeBase(t);
|
public final void | prefetch()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 void | realize()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 void | removeControllerListener(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 void | resetInterrupt()Reset the interrupted flag.
synchronized (interruptSync) {
interrupted = false;
interruptSync.notify();
}
|
protected final void | sendEvent(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 void | setClock(javax.media.Clock c)Subclass can use this to switch to a different default clock.
clock = c;
|
protected void | setMediaLength(long t)
if (clock instanceof BasicClock)
((BasicClock)clock).setMediaLength(t);
|
public void | setMediaTime(javax.media.Time when)
if (state < Realized) {
throwError(new NotRealizedError(MediaTimeError));
}
clock.setMediaTime(when);
doSetMediaTime(when);
sendEvent(new MediaTimeSetEvent(this, when));
|
public float | setRate(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 void | setStopTime(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 void | setTargetState(int state)Set the target state.
targetState = state;
|
public void | setTimeBase(javax.media.TimeBase tb)
if (state < Realized) {
throwError(new NotRealizedError(TimeBaseError));
}
clock.setTimeBase(tb);
|
public void | stop()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 void | stopAtTime()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 void | stopControllerOnly()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 void | syncStart(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 boolean | syncStartInProgress()
return (startThread != null && startThread.isAlive());
|
protected void | throwError(java.lang.Error e)
Log.dumpStack(e);
throw e;
|