xref: /illumos-gate/usr/src/lib/libdtrace_jni/java/src/org/opensolaris/os/dtrace/LocalConsumer.java (revision 3a9318192067bc7735a0a8b261c598c7d4129177)
1fb3fb4f3Stomee /*
2fb3fb4f3Stomee  * CDDL HEADER START
3fb3fb4f3Stomee  *
4fb3fb4f3Stomee  * The contents of this file are subject to the terms of the
5fb3fb4f3Stomee  * Common Development and Distribution License (the "License").
6fb3fb4f3Stomee  * You may not use this file except in compliance with the License.
7fb3fb4f3Stomee  *
8fb3fb4f3Stomee  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9fb3fb4f3Stomee  * or http://www.opensolaris.org/os/licensing.
10fb3fb4f3Stomee  * See the License for the specific language governing permissions
11fb3fb4f3Stomee  * and limitations under the License.
12fb3fb4f3Stomee  *
13fb3fb4f3Stomee  * When distributing Covered Code, include this CDDL HEADER in each
14fb3fb4f3Stomee  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15fb3fb4f3Stomee  * If applicable, add the following below this CDDL HEADER, with the
16fb3fb4f3Stomee  * fields enclosed by brackets "[]" replaced with your own identifying
17fb3fb4f3Stomee  * information: Portions Copyright [yyyy] [name of copyright owner]
18fb3fb4f3Stomee  *
19fb3fb4f3Stomee  * CDDL HEADER END
20fb3fb4f3Stomee  */
21fb3fb4f3Stomee 
22fb3fb4f3Stomee /*
23e77b06d2Stomee  * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
24fb3fb4f3Stomee  * Use is subject to license terms.
25fb3fb4f3Stomee  */
26fb3fb4f3Stomee package org.opensolaris.os.dtrace;
27fb3fb4f3Stomee 
28fb3fb4f3Stomee import java.io.*;
29fb3fb4f3Stomee import java.util.*;
30fb3fb4f3Stomee import java.net.InetAddress;
31fb3fb4f3Stomee import java.net.UnknownHostException;
32fb3fb4f3Stomee import javax.swing.event.EventListenerList;
33fb3fb4f3Stomee import java.util.logging.*;
34fb3fb4f3Stomee 
35fb3fb4f3Stomee /**
36fb3fb4f3Stomee  * Interface to the native DTrace library, each instance is a single
37fb3fb4f3Stomee  * DTrace consumer.
38fb3fb4f3Stomee  *
39fb3fb4f3Stomee  * @author Tom Erickson
40fb3fb4f3Stomee  */
41fb3fb4f3Stomee public class LocalConsumer implements Consumer {
42fb3fb4f3Stomee     //
43fb3fb4f3Stomee     // Implementation notes:
44fb3fb4f3Stomee     //
45fb3fb4f3Stomee     // libdtrace is *not* thread-safe.  You cannot make multiple calls
46fb3fb4f3Stomee     // into it simultaneously from different threads, even if those
47fb3fb4f3Stomee     // threads are operating on different dtrace_hdl_t's.  Calls to
48fb3fb4f3Stomee     // libdtrace are synchronized on a global lock, LocalConsumer.class.
49fb3fb4f3Stomee 
50fb3fb4f3Stomee     static Logger logger = Logger.getLogger(LocalConsumer.class.getName());
51fb3fb4f3Stomee 
524ae67516Stomee     // Needs to match the version in dtrace_jni.c
53e77b06d2Stomee     private static final int DTRACE_JNI_VERSION = 3;
54fb3fb4f3Stomee 
55fb3fb4f3Stomee     private static final Option[] DEFAULT_OPTIONS = new Option[] {
56fb3fb4f3Stomee 	new Option(Option.bufsize, Option.kb(256)),
57fb3fb4f3Stomee 	new Option(Option.aggsize, Option.kb(256)),
58fb3fb4f3Stomee     };
59fb3fb4f3Stomee 
_loadJniTable()60fb3fb4f3Stomee     private static native void _loadJniTable();
61fb3fb4f3Stomee 
6252aacb45Stomee     // Undocumented configuration options
63fb3fb4f3Stomee     private static boolean debug;
64fb3fb4f3Stomee     private static int maxConsumers;
65fb3fb4f3Stomee 
66fb3fb4f3Stomee     static {
LocalConsumer.configureLogging()6752aacb45Stomee 	LocalConsumer.configureLogging();
6852aacb45Stomee 	// Undocumented configuration options settable using
6952aacb45Stomee 	// java -Doption=value
LocalConsumer.getConfigurationOptions()7052aacb45Stomee 	LocalConsumer.getConfigurationOptions();
71fb3fb4f3Stomee 
72fb3fb4f3Stomee 	Utility.loadLibrary("libdtrace_jni.so.1", debug);
73fb3fb4f3Stomee 
74fb3fb4f3Stomee 	_checkVersion(DTRACE_JNI_VERSION);
7552aacb45Stomee 	_setDebug(debug);
76fb3fb4f3Stomee 	if (maxConsumers > 0) {
77fb3fb4f3Stomee 	    _setMaximumConsumers(maxConsumers);
78fb3fb4f3Stomee 	}
79fb3fb4f3Stomee 
80fb3fb4f3Stomee 	//
81fb3fb4f3Stomee 	// Last of all in case configuration options affect the loading
82fb3fb4f3Stomee 	// of the JNI table.
83fb3fb4f3Stomee 	//
_loadJniTable()84fb3fb4f3Stomee 	_loadJniTable();
85fb3fb4f3Stomee     }
86fb3fb4f3Stomee 
87fb3fb4f3Stomee     // Native JNI interface (see lib/libdtrace_jni/dtrace_jni.c)
_checkVersion(int version)88fb3fb4f3Stomee     private static native void _checkVersion(int version);
_open(OpenFlag[] flags)89fb3fb4f3Stomee     private native void _open(OpenFlag[] flags) throws DTraceException;
_compileString(String program, String[] args)90fb3fb4f3Stomee     private native Program _compileString(String program, String[] args)
91fb3fb4f3Stomee 	    throws DTraceException;
_compileFile(String path, String[] args)92fb3fb4f3Stomee     private native Program.File _compileFile(String path, String[] args)
93fb3fb4f3Stomee 	    throws DTraceException;
_exec(Program program)94fb3fb4f3Stomee     private native void _exec(Program program) throws DTraceException;
_getProgramInfo(Program program)95fb3fb4f3Stomee     private native void _getProgramInfo(Program program)
96fb3fb4f3Stomee 	    throws DTraceException;
_setOption(String option, String value)97fb3fb4f3Stomee     private native void _setOption(String option, String value)
98fb3fb4f3Stomee 	    throws DTraceException;
_getOption(String option)99fb3fb4f3Stomee     private native long _getOption(String option) throws DTraceException;
_isEnabled()100fb3fb4f3Stomee     private native boolean _isEnabled();
_checkProgramEnabling()101fb3fb4f3Stomee     private native void _checkProgramEnabling();
_go()102fb3fb4f3Stomee     private native void _go() throws DTraceException;
_stop()103fb3fb4f3Stomee     private native void _stop() throws DTraceException;
_consume()104fb3fb4f3Stomee     private native void _consume() throws DTraceException;
_interrupt()105fb3fb4f3Stomee     private native void _interrupt();
_close()106fb3fb4f3Stomee     private native void _close();
_getAggregate(AggregateSpec spec)107fb3fb4f3Stomee     private native Aggregate _getAggregate(AggregateSpec spec)
108fb3fb4f3Stomee 	    throws DTraceException;
_createProcess(String cmd)109fb3fb4f3Stomee     private native int _createProcess(String cmd) throws DTraceException;
_grabProcess(int pid)110fb3fb4f3Stomee     private native void _grabProcess(int pid) throws DTraceException;
_listProbes(List <ProbeDescription> probeList, ProbeDescription filter)111fb3fb4f3Stomee     private native void _listProbes(List <ProbeDescription> probeList,
112fb3fb4f3Stomee 	    ProbeDescription filter);
_listProbeDetail(List <Probe> probeList, ProbeDescription filter)113fb3fb4f3Stomee     private native void _listProbeDetail(List <Probe> probeList,
114fb3fb4f3Stomee 	    ProbeDescription filter);
_listCompiledProbes( List <ProbeDescription> probeList, Program program)115fb3fb4f3Stomee     private native void _listCompiledProbes(
116fb3fb4f3Stomee 	    List <ProbeDescription> probeList, Program program);
_listCompiledProbeDetail( List <Probe> probeList, Program program)117fb3fb4f3Stomee     private native void _listCompiledProbeDetail(
118fb3fb4f3Stomee 	    List <Probe> probeList, Program program);
_getVersion()119fb3fb4f3Stomee     private static native String _getVersion();
_openCount()120fb3fb4f3Stomee     private static native int _openCount();
121fb3fb4f3Stomee     //
122fb3fb4f3Stomee     // Releases memory held in the JNI layer after dtrace_close() has
123fb3fb4f3Stomee     // released critical system resources like file descriptors, and
124fb3fb4f3Stomee     // calls to libdtrace are no longer needed (or possible).
125fb3fb4f3Stomee     //
_destroy()126fb3fb4f3Stomee     private native void _destroy();
127fb3fb4f3Stomee     // Called by LogDistribution
_quantizeBucket(int i)128fb3fb4f3Stomee     static native long _quantizeBucket(int i);
129fb3fb4f3Stomee     //
130fb3fb4f3Stomee     // Cannot be static because the necessary dtrace handle is specific
131fb3fb4f3Stomee     // to this Consumer.
132fb3fb4f3Stomee     //
_lookupKernelFunction(Number address)133fb3fb4f3Stomee     private native String _lookupKernelFunction(Number address);
_lookupUserFunction(int pid, Number address)134fb3fb4f3Stomee     private native String _lookupUserFunction(int pid, Number address);
_getExecutableName()135fb3fb4f3Stomee     private static native String _getExecutableName();
136fb3fb4f3Stomee 
137fb3fb4f3Stomee     // Undocumented configuration options
_setMaximumConsumers(int max)138fb3fb4f3Stomee     private static native void _setMaximumConsumers(int max);
_setDebug(boolean debug)139fb3fb4f3Stomee     private static native void _setDebug(boolean debug);
140fb3fb4f3Stomee 
141fb3fb4f3Stomee     protected EventListenerList listenerList;
142fb3fb4f3Stomee     protected ExceptionHandler exceptionHandler;
143fb3fb4f3Stomee 
144fb3fb4f3Stomee     private int _handle = -1;    // native C identifier (do not modify)
145fb3fb4f3Stomee     private final Identifier id; // java identifier
146fb3fb4f3Stomee 
147fb3fb4f3Stomee     private enum State {
148fb3fb4f3Stomee 	INIT,
149fb3fb4f3Stomee 	OPEN,
150fb3fb4f3Stomee 	COMPILED,
151fb3fb4f3Stomee 	GO,
152fb3fb4f3Stomee 	STARTED,
153fb3fb4f3Stomee 	STOPPED,
154fb3fb4f3Stomee 	CLOSED
155fb3fb4f3Stomee     }
156fb3fb4f3Stomee 
157fb3fb4f3Stomee     private State state = State.INIT;
158fb3fb4f3Stomee     private boolean stopCalled;
15943fb4b48Stomee     private boolean abortCalled;
160fb3fb4f3Stomee 
161fb3fb4f3Stomee     //
162fb3fb4f3Stomee     // Per-consumer lock used in native code to prevent conflict between
163fb3fb4f3Stomee     // the native consumer loop and the getAggregate() thread without
164fb3fb4f3Stomee     // locking this LocalConsumer.  A distinct per-consumer lock allows
165fb3fb4f3Stomee     // the stop() method to be synchronized without causing deadlock
166fb3fb4f3Stomee     // when the consumer loop grabs the per-consumer lock before
167fb3fb4f3Stomee     // dtrace_work().
168fb3fb4f3Stomee     //
169fb3fb4f3Stomee     private Object consumerLock;
17052aacb45Stomee 
171fb3fb4f3Stomee     //
17252aacb45Stomee     // stopLock is a synchronization lock used to ensure that the stop()
17352aacb45Stomee     // method does not return until this consumer has actually stopped.
17452aacb45Stomee     // Correct lock ordering is needed to ensure that listeners cannot
17552aacb45Stomee     // deadlock this consumer:
17652aacb45Stomee     // 1. stop() grabs the lock on this consumer before determining if
17752aacb45Stomee     //    this consumer is running (to ensure valid state).
17852aacb45Stomee     // 2. Once stop() determines that this consumer is actually running,
17952aacb45Stomee     //    it releases the lock on this consumer.  Failing to release the
18052aacb45Stomee     //    lock makes it possible for a ConsumerListener to deadlock this
18152aacb45Stomee     //    consumer by calling any synchronized LocalConcumer method
18252aacb45Stomee     //    (because the listener called by the worker thread prevents the
18352aacb45Stomee     //    worker thread from finishing while it waits for stop() to
18452aacb45Stomee     //    release the lock, which it will never do until the worker
18552aacb45Stomee     //    thread finishes).
18652aacb45Stomee     // 3. stop() interrupts this consumer and grabs the stopLock, then
18752aacb45Stomee     //    waits on the stopLock for this consumer to stop (i.e. for the
18852aacb45Stomee     //    worker thread to finish).
18952aacb45Stomee     // 4. The interrupted worker thread grabs the stopLock when it
19052aacb45Stomee     //    finishes so it can notify waiters on the stopLock (in this
19152aacb45Stomee     //    case the stop() method) that the worker thread is finished.
19252aacb45Stomee     //    The workEnded flag (whose access is protected by the
19352aacb45Stomee     //    stopLock), is used in case the interrupted worker thread
19452aacb45Stomee     //    finishes and grabs the stopLock before the stop() method does.
19552aacb45Stomee     //    Setting the flag in that case tells the stop() method it has
19652aacb45Stomee     //    nothing to wait for (otherwise stop() would wait forever,
19752aacb45Stomee     //    since there is no one left after the worker thread finishes to
19852aacb45Stomee     //    notify the stop() method to stop waiting).
19952aacb45Stomee     // 5. The worker thread updates the state member to STOPPED and
20052aacb45Stomee     //    notifies listeners while it holds the stopLock and before it
20152aacb45Stomee     //    notifies waiters on the stopLock.  This is to ensure that
20252aacb45Stomee     //    state has been updated to STOPPED and that listeners have
20352aacb45Stomee     //    executed consumerStopped() before the stop() method returns,
20452aacb45Stomee     //    to ensure valid state and in case the caller of stop() is
20552aacb45Stomee     //    relying on anything having been done by consumerStopped()
20652aacb45Stomee     //    before it proceeds to the next statement.
20752aacb45Stomee     // 6. The worker thread notifies waiters on the stopLock before
20852aacb45Stomee     //    releasing it.  stop() returns.
209fb3fb4f3Stomee     //
210fb3fb4f3Stomee     private Object stopLock;
211fb3fb4f3Stomee     private boolean workEnded;
212fb3fb4f3Stomee 
213fb3fb4f3Stomee     private static int sequence = 0;
214fb3fb4f3Stomee 
21552aacb45Stomee     private static void
configureLogging()21652aacb45Stomee     configureLogging()
21752aacb45Stomee     {
21852aacb45Stomee 	logger.setUseParentHandlers(false);
21952aacb45Stomee 	Handler handler = new ConsoleHandler();
22052aacb45Stomee 	handler.setLevel(Level.ALL);
22152aacb45Stomee 	logger.addHandler(handler);
22252aacb45Stomee         logger.setLevel(Level.OFF);
22352aacb45Stomee     }
22452aacb45Stomee 
22552aacb45Stomee     private static Integer
getIntegerProperty(String name)22652aacb45Stomee     getIntegerProperty(String name)
22752aacb45Stomee     {
22852aacb45Stomee 	Integer value = null;
22952aacb45Stomee 	String property = System.getProperty(name);
23052aacb45Stomee 	if (property != null && property.length() != 0) {
23152aacb45Stomee 	    try {
23252aacb45Stomee 		value = Integer.parseInt(property);
23352aacb45Stomee 		System.out.println(name + "=" + value);
23452aacb45Stomee 	    } catch (NumberFormatException e) {
23552aacb45Stomee 		System.err.println("Warning: property ignored: " +
23652aacb45Stomee 			name + "=" + property);
23752aacb45Stomee 	    }
23852aacb45Stomee 	}
23952aacb45Stomee 	return value;
24052aacb45Stomee     }
24152aacb45Stomee 
24252aacb45Stomee     private static void
getConfigurationOptions()24352aacb45Stomee     getConfigurationOptions()
24452aacb45Stomee     {
24552aacb45Stomee 	Integer property;
24652aacb45Stomee 	property = getIntegerProperty("JAVA_DTRACE_API_DEBUG");
24752aacb45Stomee 	if (property != null) {
24852aacb45Stomee 	    debug = (property != 0);
24952aacb45Stomee 	}
25052aacb45Stomee 	property = getIntegerProperty("JAVA_DTRACE_MAX_CONSUMERS");
25152aacb45Stomee 	if (property != null) {
25252aacb45Stomee 	    maxConsumers = property;
25352aacb45Stomee 	}
25452aacb45Stomee     }
25552aacb45Stomee 
256fb3fb4f3Stomee     /**
257fb3fb4f3Stomee      * Creates a consumer that interacts with the native DTrace library
258fb3fb4f3Stomee      * on the local system.
259fb3fb4f3Stomee      */
260fb3fb4f3Stomee     public
LocalConsumer()261fb3fb4f3Stomee     LocalConsumer()
262fb3fb4f3Stomee     {
263fb3fb4f3Stomee 	id = new LocalConsumer.Identifier(this);
264fb3fb4f3Stomee 	consumerLock = new Object();
265fb3fb4f3Stomee 	stopLock = new Object();
266fb3fb4f3Stomee 	listenerList = new EventListenerList();
267fb3fb4f3Stomee     }
268fb3fb4f3Stomee 
269fb3fb4f3Stomee     /**
270fb3fb4f3Stomee      * Called by native C code only
271fb3fb4f3Stomee      */
272fb3fb4f3Stomee     private int
getHandle()273fb3fb4f3Stomee     getHandle()
274fb3fb4f3Stomee     {
275fb3fb4f3Stomee 	return _handle;
276fb3fb4f3Stomee     }
277fb3fb4f3Stomee 
278fb3fb4f3Stomee     /**
279fb3fb4f3Stomee      * Called by native C code only
280fb3fb4f3Stomee      */
281fb3fb4f3Stomee     private void
setHandle(int n)282fb3fb4f3Stomee     setHandle(int n)
283fb3fb4f3Stomee     {
284fb3fb4f3Stomee 	_handle = n;
285fb3fb4f3Stomee     }
286fb3fb4f3Stomee 
287fb3fb4f3Stomee     public synchronized void
open(OpenFlag .... flags)288fb3fb4f3Stomee     open(OpenFlag ... flags) throws DTraceException
289fb3fb4f3Stomee     {
290fb3fb4f3Stomee 	if (state == State.CLOSED) {
291fb3fb4f3Stomee 	    throw new IllegalStateException("cannot reopen a closed consumer");
292fb3fb4f3Stomee 	}
293fb3fb4f3Stomee 	if (state != State.INIT) {
294fb3fb4f3Stomee 	    throw new IllegalStateException("consumer already open");
295fb3fb4f3Stomee 	}
296fb3fb4f3Stomee 
297fb3fb4f3Stomee 	for (OpenFlag flag : flags) {
298fb3fb4f3Stomee 	    if (flag == null) {
299fb3fb4f3Stomee 		throw new NullPointerException("open flag is null");
300fb3fb4f3Stomee 	    }
301fb3fb4f3Stomee 	}
302fb3fb4f3Stomee 
303fb3fb4f3Stomee 	synchronized (LocalConsumer.class) {
304fb3fb4f3Stomee 	    _open(flags);
305fb3fb4f3Stomee 	}
306fb3fb4f3Stomee 
307fb3fb4f3Stomee 	state = State.OPEN;
308fb3fb4f3Stomee 	setOptions(DEFAULT_OPTIONS);
309fb3fb4f3Stomee 
31043fb4b48Stomee 	if (abortCalled) {
31143fb4b48Stomee 	    _interrupt();
31243fb4b48Stomee 	}
31343fb4b48Stomee 
314fb3fb4f3Stomee 	if (logger.isLoggable(Level.INFO)) {
315fb3fb4f3Stomee 	    logger.info("consumer table count: " + _openCount());
316fb3fb4f3Stomee 	}
317fb3fb4f3Stomee     }
318fb3fb4f3Stomee 
319fb3fb4f3Stomee     private synchronized void
checkCompile()320fb3fb4f3Stomee     checkCompile()
321fb3fb4f3Stomee     {
322fb3fb4f3Stomee 	switch (state) {
323fb3fb4f3Stomee 	    case INIT:
324fb3fb4f3Stomee 		throw new IllegalStateException("consumer not open");
325fb3fb4f3Stomee 	    case OPEN:
326fb3fb4f3Stomee 	    case COMPILED: // caller may compile more than one program
327fb3fb4f3Stomee 		break;
328fb3fb4f3Stomee 	    case GO:
329fb3fb4f3Stomee 	    case STARTED:
330fb3fb4f3Stomee 		throw new IllegalStateException("go() already called");
331fb3fb4f3Stomee 	    case STOPPED:
332fb3fb4f3Stomee 		throw new IllegalStateException("consumer stopped");
333fb3fb4f3Stomee 	    case CLOSED:
334fb3fb4f3Stomee 		throw new IllegalStateException("consumer closed");
335fb3fb4f3Stomee 	}
336fb3fb4f3Stomee     }
337fb3fb4f3Stomee 
338fb3fb4f3Stomee     public synchronized Program
compile(String program, String ... macroArgs)339fb3fb4f3Stomee     compile(String program, String ... macroArgs) throws DTraceException
340fb3fb4f3Stomee     {
341fb3fb4f3Stomee 	if (program == null) {
342fb3fb4f3Stomee 	    throw new NullPointerException("program string is null");
343fb3fb4f3Stomee 	}
344fb3fb4f3Stomee 	checkCompile();
345fb3fb4f3Stomee 	Program p = null;
346fb3fb4f3Stomee 
347fb3fb4f3Stomee 	String[] argv = null;
348fb3fb4f3Stomee 	if (macroArgs != null) {
349fb3fb4f3Stomee 	    for (String macroArg : macroArgs) {
350fb3fb4f3Stomee 		if (macroArg == null) {
351fb3fb4f3Stomee 		    throw new NullPointerException("macro argument is null");
352fb3fb4f3Stomee 		}
353fb3fb4f3Stomee 	    }
354fb3fb4f3Stomee 	    argv = new String[macroArgs.length + 1];
355fb3fb4f3Stomee 	    synchronized (LocalConsumer.class) {
356fb3fb4f3Stomee 		//
357fb3fb4f3Stomee 		// Could be an application with an embedded JVM, not
358fb3fb4f3Stomee 		// necessarily "java".
359fb3fb4f3Stomee 		//
360fb3fb4f3Stomee 		argv[0] = _getExecutableName();
361fb3fb4f3Stomee 	    }
362fb3fb4f3Stomee 	    System.arraycopy(macroArgs, 0, argv, 1, macroArgs.length);
363fb3fb4f3Stomee 	} else {
364fb3fb4f3Stomee 	    synchronized (LocalConsumer.class) {
365fb3fb4f3Stomee 		argv = new String[] { _getExecutableName() };
366fb3fb4f3Stomee 	    }
367fb3fb4f3Stomee 	}
368fb3fb4f3Stomee 	synchronized (LocalConsumer.class) {
369fb3fb4f3Stomee 	    p = _compileString(program, argv);
370fb3fb4f3Stomee 	}
371fb3fb4f3Stomee 	p.consumerID = id;
372fb3fb4f3Stomee 	p.contents = program;
373fb3fb4f3Stomee 	p.validate();
374fb3fb4f3Stomee 	state = State.COMPILED;
375fb3fb4f3Stomee 
376fb3fb4f3Stomee 	return p;
377fb3fb4f3Stomee     }
378fb3fb4f3Stomee 
379fb3fb4f3Stomee     public synchronized Program
compile(File program, String ... macroArgs)380fb3fb4f3Stomee     compile(File program, String ... macroArgs) throws DTraceException,
381fb3fb4f3Stomee             IOException, SecurityException
382fb3fb4f3Stomee     {
383fb3fb4f3Stomee 	if (program == null) {
384fb3fb4f3Stomee 	    throw new NullPointerException("program file is null");
385fb3fb4f3Stomee 	}
386fb3fb4f3Stomee 	if (!program.canRead()) {
387fb3fb4f3Stomee 	    throw new FileNotFoundException("failed to open " +
388fb3fb4f3Stomee 		    program.getName());
389fb3fb4f3Stomee 	}
390fb3fb4f3Stomee 	checkCompile();
391fb3fb4f3Stomee 	Program.File p = null;
392fb3fb4f3Stomee 
393fb3fb4f3Stomee 	String[] argv = null;
394fb3fb4f3Stomee 	if (macroArgs != null) {
395fb3fb4f3Stomee 	    for (String macroArg : macroArgs) {
396fb3fb4f3Stomee 		if (macroArg == null) {
397fb3fb4f3Stomee 		    throw new NullPointerException("macro argument is null");
398fb3fb4f3Stomee 		}
399fb3fb4f3Stomee 	    }
400fb3fb4f3Stomee 	    argv = new String[macroArgs.length + 1];
401fb3fb4f3Stomee 	    argv[0] = program.getPath();
402fb3fb4f3Stomee 	    System.arraycopy(macroArgs, 0, argv, 1, macroArgs.length);
403fb3fb4f3Stomee 	} else {
404fb3fb4f3Stomee 	    macroArgs = new String[] { program.getPath() };
405fb3fb4f3Stomee 	}
406fb3fb4f3Stomee 	synchronized (LocalConsumer.class) {
407fb3fb4f3Stomee 	    p = _compileFile(program.getPath(), argv);
408fb3fb4f3Stomee 	}
409fb3fb4f3Stomee 	p.consumerID = id;
410fb3fb4f3Stomee 	p.contents = Program.getProgramString(program);
411fb3fb4f3Stomee 	p.file = program;
412fb3fb4f3Stomee 	p.validate();
41391cfa10aStomee 	p.validateFile();
414fb3fb4f3Stomee 	state = State.COMPILED;
415fb3fb4f3Stomee 
416fb3fb4f3Stomee 	return p;
417fb3fb4f3Stomee     }
418fb3fb4f3Stomee 
419fb3fb4f3Stomee     private synchronized void
checkProgram(Program program)420fb3fb4f3Stomee     checkProgram(Program program)
421fb3fb4f3Stomee     {
422fb3fb4f3Stomee 	if (program == null) {
423fb3fb4f3Stomee 	    throw new NullPointerException("program is null");
424fb3fb4f3Stomee 	}
425fb3fb4f3Stomee 	if (!id.equals(program.consumerID)) {
426fb3fb4f3Stomee 	    throw new IllegalArgumentException("program not compiled " +
427fb3fb4f3Stomee 		    "by this consumer");
428fb3fb4f3Stomee 	}
429fb3fb4f3Stomee     }
430fb3fb4f3Stomee 
431fb3fb4f3Stomee     public void
enable()432fb3fb4f3Stomee     enable() throws DTraceException
433fb3fb4f3Stomee     {
434fb3fb4f3Stomee 	enable(null);
435fb3fb4f3Stomee     }
436fb3fb4f3Stomee 
437fb3fb4f3Stomee     public synchronized void
enable(Program program)438fb3fb4f3Stomee     enable(Program program) throws DTraceException
439fb3fb4f3Stomee     {
440fb3fb4f3Stomee 	switch (state) {
441fb3fb4f3Stomee 	    case INIT:
442fb3fb4f3Stomee 		throw new IllegalStateException("consumer not open");
443fb3fb4f3Stomee 	    case OPEN:
444fb3fb4f3Stomee 		throw new IllegalStateException("no compiled program");
445fb3fb4f3Stomee 	    case COMPILED:
446fb3fb4f3Stomee 		break;
447fb3fb4f3Stomee 	    case GO:
448fb3fb4f3Stomee 	    case STARTED:
449fb3fb4f3Stomee 		throw new IllegalStateException("go() already called");
450fb3fb4f3Stomee 	    case STOPPED:
451fb3fb4f3Stomee 		throw new IllegalStateException("consumer stopped");
452fb3fb4f3Stomee 	    case CLOSED:
453fb3fb4f3Stomee 		throw new IllegalStateException("consumer closed");
454fb3fb4f3Stomee 	}
455fb3fb4f3Stomee 
456fb3fb4f3Stomee 	// Compile all programs if null
457fb3fb4f3Stomee 	if (program != null) {
458fb3fb4f3Stomee 	    checkProgram(program);
459fb3fb4f3Stomee 	}
460fb3fb4f3Stomee 
461fb3fb4f3Stomee 	//
462fb3fb4f3Stomee 	// Left to native code to throw IllegalArgumentException if the
463fb3fb4f3Stomee 	// program is already enabled, since only the native code knows
464fb3fb4f3Stomee 	// the enabled state.
465fb3fb4f3Stomee 	//
466fb3fb4f3Stomee 	synchronized (LocalConsumer.class) {
467fb3fb4f3Stomee 	    _exec(program);
468fb3fb4f3Stomee 	}
469fb3fb4f3Stomee     }
470fb3fb4f3Stomee 
471fb3fb4f3Stomee     public synchronized void
getProgramInfo(Program program)472fb3fb4f3Stomee     getProgramInfo(Program program) throws DTraceException
473fb3fb4f3Stomee     {
474fb3fb4f3Stomee 	checkProgram(program);
475fb3fb4f3Stomee 	if (state == State.CLOSED) {
476fb3fb4f3Stomee 	    throw new IllegalStateException("consumer closed");
477fb3fb4f3Stomee 	}
478fb3fb4f3Stomee 
479fb3fb4f3Stomee 	//
480fb3fb4f3Stomee 	// The given program was compiled by this consumer, so we can
481fb3fb4f3Stomee 	// assert the following:
482fb3fb4f3Stomee 	//
483fb3fb4f3Stomee 	assert ((state != State.INIT) && (state != State.OPEN));
484fb3fb4f3Stomee 
485fb3fb4f3Stomee 	synchronized (LocalConsumer.class) {
486fb3fb4f3Stomee 	    _getProgramInfo(program);
487fb3fb4f3Stomee 	}
488fb3fb4f3Stomee     }
489fb3fb4f3Stomee 
490fb3fb4f3Stomee     private void
setOptions(Option[] options)491fb3fb4f3Stomee     setOptions(Option[] options) throws DTraceException
492fb3fb4f3Stomee     {
493fb3fb4f3Stomee 	for (Option o : options) {
49452aacb45Stomee 	    setOption(o.getName(), o.getValue());
495fb3fb4f3Stomee 	}
496fb3fb4f3Stomee     }
497fb3fb4f3Stomee 
498fb3fb4f3Stomee     public void
setOption(String option)499fb3fb4f3Stomee     setOption(String option) throws DTraceException
500fb3fb4f3Stomee     {
501fb3fb4f3Stomee 	setOption(option, Option.VALUE_SET);
502fb3fb4f3Stomee     }
503fb3fb4f3Stomee 
504fb3fb4f3Stomee     public void
unsetOption(String option)505fb3fb4f3Stomee     unsetOption(String option) throws DTraceException
506fb3fb4f3Stomee     {
507fb3fb4f3Stomee 	setOption(option, Option.VALUE_UNSET);
508fb3fb4f3Stomee     }
509fb3fb4f3Stomee 
510fb3fb4f3Stomee     public synchronized void
setOption(String option, String value)511fb3fb4f3Stomee     setOption(String option, String value) throws DTraceException
512fb3fb4f3Stomee     {
513fb3fb4f3Stomee 	if (option == null) {
514fb3fb4f3Stomee 	    throw new NullPointerException("option is null");
515fb3fb4f3Stomee 	}
516fb3fb4f3Stomee 	if (value == null) {
517fb3fb4f3Stomee 	    throw new NullPointerException("option value is null");
518fb3fb4f3Stomee 	}
519fb3fb4f3Stomee 
520fb3fb4f3Stomee 	switch (state) {
521fb3fb4f3Stomee 	    case INIT:
522fb3fb4f3Stomee 		throw new IllegalStateException("consumer not open");
523fb3fb4f3Stomee 	    case OPEN:
524fb3fb4f3Stomee 	    case COMPILED:
525fb3fb4f3Stomee 	    case GO:
526fb3fb4f3Stomee 	    case STARTED: // Some options can be set on a running consumer
527fb3fb4f3Stomee 	    case STOPPED: // Allowed (may affect getAggregate())
528fb3fb4f3Stomee 		break;
529fb3fb4f3Stomee 	    case CLOSED:
530fb3fb4f3Stomee 		throw new IllegalStateException("consumer closed");
531fb3fb4f3Stomee 	}
532fb3fb4f3Stomee 
533fb3fb4f3Stomee 	synchronized (LocalConsumer.class) {
534fb3fb4f3Stomee 	    _setOption(option, value);
535fb3fb4f3Stomee 	}
536fb3fb4f3Stomee     }
537fb3fb4f3Stomee 
538fb3fb4f3Stomee     public synchronized long
getOption(String option)539fb3fb4f3Stomee     getOption(String option) throws DTraceException
540fb3fb4f3Stomee     {
541fb3fb4f3Stomee 	if (option == null) {
542fb3fb4f3Stomee 	    throw new NullPointerException("option is null");
543fb3fb4f3Stomee 	}
544fb3fb4f3Stomee 
545fb3fb4f3Stomee 	switch (state) {
546fb3fb4f3Stomee 	    case INIT:
547fb3fb4f3Stomee 		throw new IllegalStateException("consumer not open");
548fb3fb4f3Stomee 	    case OPEN:
549fb3fb4f3Stomee 	    case COMPILED:
550fb3fb4f3Stomee 	    case GO:
551fb3fb4f3Stomee 	    case STARTED:
552fb3fb4f3Stomee 	    case STOPPED:
553fb3fb4f3Stomee 		break;
554fb3fb4f3Stomee 	    case CLOSED:
555fb3fb4f3Stomee 		throw new IllegalStateException("consumer closed");
556fb3fb4f3Stomee 	}
557fb3fb4f3Stomee 
558fb3fb4f3Stomee 	long value;
559fb3fb4f3Stomee 	synchronized (LocalConsumer.class) {
560fb3fb4f3Stomee 	    value = _getOption(option);
561fb3fb4f3Stomee 	}
562fb3fb4f3Stomee 	return value;
563fb3fb4f3Stomee     }
564fb3fb4f3Stomee 
565fb3fb4f3Stomee     public final synchronized boolean
isOpen()566fb3fb4f3Stomee     isOpen()
567fb3fb4f3Stomee     {
568fb3fb4f3Stomee 	return ((state != State.INIT) && (state != State.CLOSED));
569fb3fb4f3Stomee     }
570fb3fb4f3Stomee 
571fb3fb4f3Stomee     public final synchronized boolean
isEnabled()572fb3fb4f3Stomee     isEnabled()
573fb3fb4f3Stomee     {
574fb3fb4f3Stomee 	if (state != State.COMPILED) {
575fb3fb4f3Stomee 	    return false;
576fb3fb4f3Stomee 	}
577fb3fb4f3Stomee 
578fb3fb4f3Stomee 	return _isEnabled();
579fb3fb4f3Stomee     }
580fb3fb4f3Stomee 
581fb3fb4f3Stomee     public final synchronized boolean
isRunning()582fb3fb4f3Stomee     isRunning()
583fb3fb4f3Stomee     {
584fb3fb4f3Stomee 	return (state == State.STARTED);
585fb3fb4f3Stomee     }
586fb3fb4f3Stomee 
587fb3fb4f3Stomee     public final synchronized boolean
isClosed()588fb3fb4f3Stomee     isClosed()
589fb3fb4f3Stomee     {
590fb3fb4f3Stomee 	return (state == State.CLOSED);
591fb3fb4f3Stomee     }
592fb3fb4f3Stomee 
593fb3fb4f3Stomee     /**
594fb3fb4f3Stomee      * Called in the runnable target of the thread returned by {@link
595fb3fb4f3Stomee      * #createThread()} to run this DTrace consumer.
596fb3fb4f3Stomee      *
597fb3fb4f3Stomee      * @see #createThread()
598fb3fb4f3Stomee      */
599fb3fb4f3Stomee     protected final void
work()600fb3fb4f3Stomee     work()
601fb3fb4f3Stomee     {
602fb3fb4f3Stomee 	try {
603fb3fb4f3Stomee 	    synchronized (this) {
604fb3fb4f3Stomee 		if (state != State.GO) {
605fb3fb4f3Stomee 		    //
606fb3fb4f3Stomee 		    // stop() was called after go() but before the
607fb3fb4f3Stomee 		    // consumer started
608fb3fb4f3Stomee 		    //
609fb3fb4f3Stomee 		    return; // executes finally block before returning
610fb3fb4f3Stomee 		}
611fb3fb4f3Stomee 
612fb3fb4f3Stomee 		state = State.STARTED;
613fb3fb4f3Stomee 		fireConsumerStarted(new ConsumerEvent(this,
614fb3fb4f3Stomee 			System.nanoTime()));
615fb3fb4f3Stomee 	    }
616fb3fb4f3Stomee 
617fb3fb4f3Stomee 	    //
618fb3fb4f3Stomee 	    // We should not prevent other consumers from running
619fb3fb4f3Stomee 	    // concurrently while this consumer blocks on the native
620fb3fb4f3Stomee 	    // consumer loop.  Instead, native code will acquire the
621fb3fb4f3Stomee 	    // LocalConsumer.class monitor as needed before calling
622fb3fb4f3Stomee 	    // libdtrace functions.
623fb3fb4f3Stomee 	    //
624fb3fb4f3Stomee 	    _consume();
625fb3fb4f3Stomee 
626fb3fb4f3Stomee 	} catch (Throwable e) {
627fb3fb4f3Stomee 	    if (exceptionHandler != null) {
628fb3fb4f3Stomee 		exceptionHandler.handleException(e);
629fb3fb4f3Stomee 	    } else {
630fb3fb4f3Stomee 		e.printStackTrace();
631fb3fb4f3Stomee 	    }
632fb3fb4f3Stomee 	} finally {
633fb3fb4f3Stomee 	    synchronized (stopLock) {
63452aacb45Stomee 		// Notify listeners while holding stopLock to guarantee
63552aacb45Stomee 		// that listeners finish executing consumerStopped()
63652aacb45Stomee 		// before the stop() method returns.
637fb3fb4f3Stomee 		synchronized (this) {
6384d0eb50eSRichard PALO 		    if (state == State.STOPPED || state == State.CLOSED) {
639127bbe13Stomee 			//
640127bbe13Stomee 			// This consumer was stopped just after calling
641127bbe13Stomee 			// go() but before starting (the premature return
642127bbe13Stomee 			// case at the top of this work() method). It is
643127bbe13Stomee 			// possible to call close() on a consumer that has
644127bbe13Stomee 			// been stopped before starting. In that case the
645127bbe13Stomee 			// premature return above still takes us here in the
646127bbe13Stomee 			// finally clause, and we must not revert the CLOSED
647127bbe13Stomee 			// state to STOPPED.
648127bbe13Stomee 			//
649127bbe13Stomee 		    } else {
650fb3fb4f3Stomee 			state = State.STOPPED;
651fb3fb4f3Stomee 			fireConsumerStopped(new ConsumerEvent(this,
652fb3fb4f3Stomee 				System.nanoTime()));
653fb3fb4f3Stomee 		    }
654127bbe13Stomee 		}
65552aacb45Stomee 
65652aacb45Stomee 		// Notify the stop() method to stop waiting
65752aacb45Stomee 		workEnded = true;
65852aacb45Stomee 		stopLock.notifyAll();
659fb3fb4f3Stomee 	    }
660fb3fb4f3Stomee 	}
661fb3fb4f3Stomee     }
662fb3fb4f3Stomee 
663fb3fb4f3Stomee     /**
664fb3fb4f3Stomee      * Creates the background thread started by {@link #go()} to run
665fb3fb4f3Stomee      * this consumer.  Override this method if you need to set
666fb3fb4f3Stomee      * non-default {@code Thread} options or create the thread in a
667fb3fb4f3Stomee      * {@code ThreadGroup}.  If you don't need to create the thread
668fb3fb4f3Stomee      * yourself, set the desired options on {@code super.createThread()}
669fb3fb4f3Stomee      * before returning it.  Otherwise, the {@code Runnable} target of
670fb3fb4f3Stomee      * the created thread must call {@link #work()} in order to run this
671fb3fb4f3Stomee      * DTrace consumer.  For example, to modify the default background
672fb3fb4f3Stomee      * consumer thread:
673fb3fb4f3Stomee      * <pre><code>
674fb3fb4f3Stomee      *	protected Thread
675fb3fb4f3Stomee      *	createThread()
676fb3fb4f3Stomee      *	{
677fb3fb4f3Stomee      *		Thread t = super.createThread();
678fb3fb4f3Stomee      *		t.setPriority(Thread.MIN_PRIORITY);
679fb3fb4f3Stomee      *		return t;
680fb3fb4f3Stomee      *	}
681fb3fb4f3Stomee      * </code></pre>
682fb3fb4f3Stomee      * Or if you need to create your own thread:
683*3a931819SPeter Tribble      * <pre><code>
684fb3fb4f3Stomee      *	protected Thread
685fb3fb4f3Stomee      *	createThread()
686fb3fb4f3Stomee      *	{
687fb3fb4f3Stomee      *		Runnable target = new Runnable() {
688fb3fb4f3Stomee      *			public void run() {
689fb3fb4f3Stomee      *				work();
690fb3fb4f3Stomee      *			}
691fb3fb4f3Stomee      *		};
692fb3fb4f3Stomee      *		String name = "Consumer " + UserApplication.sequence++;
693fb3fb4f3Stomee      *		Thread t = new Thread(UserApplication.threadGroup,
694fb3fb4f3Stomee      *			target, name);
695fb3fb4f3Stomee      *		return t;
696fb3fb4f3Stomee      *	}
697fb3fb4f3Stomee      * </code></pre>
698fb3fb4f3Stomee      * Do not start the returned thread, otherwise {@code go()} will
699fb3fb4f3Stomee      * throw an {@link IllegalThreadStateException} when it tries to
700fb3fb4f3Stomee      * start the returned thread a second time.
701fb3fb4f3Stomee      */
702fb3fb4f3Stomee     protected Thread
createThread()703fb3fb4f3Stomee     createThread()
704fb3fb4f3Stomee     {
705fb3fb4f3Stomee 	Thread t = new Thread(new Runnable() {
706fb3fb4f3Stomee 	    public void run() {
707fb3fb4f3Stomee 		work();
708fb3fb4f3Stomee 	    }
709fb3fb4f3Stomee 	}, "DTrace consumer " + id);
710fb3fb4f3Stomee 	return t;
711fb3fb4f3Stomee     }
712fb3fb4f3Stomee 
713fb3fb4f3Stomee     /**
714*3a931819SPeter Tribble      * {@inheritDoc}
715fb3fb4f3Stomee      * @throws IllegalThreadStateException if a subclass calls {@link
716fb3fb4f3Stomee      * Thread#start()} on the value of {@link #createThread()}
717fb3fb4f3Stomee      * @see #createThread()
718fb3fb4f3Stomee      */
719fb3fb4f3Stomee     public void
go()720fb3fb4f3Stomee     go() throws DTraceException
721fb3fb4f3Stomee     {
722fb3fb4f3Stomee 	go(null);
723fb3fb4f3Stomee     }
724fb3fb4f3Stomee 
725fb3fb4f3Stomee     /**
726*3a931819SPeter Tribble      * {@inheritDoc}
727fb3fb4f3Stomee      * @throws IllegalThreadStateException if a subclass calls {@link
728fb3fb4f3Stomee      * Thread#start()} on the value of {@link #createThread()}
729fb3fb4f3Stomee      * @see #createThread()
730fb3fb4f3Stomee      */
731fb3fb4f3Stomee     public synchronized void
go(ExceptionHandler h)732fb3fb4f3Stomee     go(ExceptionHandler h) throws DTraceException
733fb3fb4f3Stomee     {
734fb3fb4f3Stomee 	switch (state) {
735fb3fb4f3Stomee 	    case INIT:
736fb3fb4f3Stomee 		throw new IllegalStateException("consumer not open");
737fb3fb4f3Stomee 	    case OPEN:
738fb3fb4f3Stomee 		throw new IllegalStateException("no compiled program");
739fb3fb4f3Stomee 	    case COMPILED:
740fb3fb4f3Stomee 		//
741fb3fb4f3Stomee 		// Throws IllegalStateException if not all compiled programs are
742fb3fb4f3Stomee 		// also enabled.  Does not make any calls to libdtrace.
743fb3fb4f3Stomee 		//
744fb3fb4f3Stomee 		_checkProgramEnabling();
745fb3fb4f3Stomee 		break;
746fb3fb4f3Stomee 	    case GO:
747fb3fb4f3Stomee 	    case STARTED:
748fb3fb4f3Stomee 		throw new IllegalStateException("go() already called");
749fb3fb4f3Stomee 	    case STOPPED:
750fb3fb4f3Stomee 		throw new IllegalStateException("consumer stopped");
751fb3fb4f3Stomee 	    case CLOSED:
752fb3fb4f3Stomee 		throw new IllegalStateException("consumer closed");
753fb3fb4f3Stomee 	    default:
754fb3fb4f3Stomee 		throw new IllegalArgumentException("unknown state: " + state);
755fb3fb4f3Stomee 	}
756fb3fb4f3Stomee 
757fb3fb4f3Stomee 	synchronized (LocalConsumer.class) {
758fb3fb4f3Stomee 	    _go();
759fb3fb4f3Stomee 	}
760fb3fb4f3Stomee 
761fb3fb4f3Stomee 	state = State.GO;
762fb3fb4f3Stomee 	exceptionHandler = h;
763fb3fb4f3Stomee 	Thread t = createThread();
764fb3fb4f3Stomee 	t.start();
765fb3fb4f3Stomee     }
766fb3fb4f3Stomee 
767382dbd46Stomee     /**
768*3a931819SPeter Tribble      * {@inheritDoc}
769382dbd46Stomee      *
770382dbd46Stomee      * @throws IllegalThreadStateException if attempting to {@code
771382dbd46Stomee      * stop()} a running consumer while holding the lock on that
772382dbd46Stomee      * consumer
773382dbd46Stomee      */
77452aacb45Stomee     public void
stop()775fb3fb4f3Stomee     stop()
776fb3fb4f3Stomee     {
77752aacb45Stomee 	boolean running = false;
77852aacb45Stomee 
77952aacb45Stomee 	synchronized (this) {
780fb3fb4f3Stomee 	    switch (state) {
781fb3fb4f3Stomee 		case INIT:
782fb3fb4f3Stomee 		    throw new IllegalStateException("consumer not open");
783fb3fb4f3Stomee 		case OPEN:
784fb3fb4f3Stomee 		case COMPILED:
785fb3fb4f3Stomee 		    throw new IllegalStateException("go() not called");
786fb3fb4f3Stomee 		case GO:
787fb3fb4f3Stomee 		    try {
788fb3fb4f3Stomee 			synchronized (LocalConsumer.class) {
789fb3fb4f3Stomee 			    _stop();
790fb3fb4f3Stomee 			}
791fb3fb4f3Stomee 			state = State.STOPPED;
792127bbe13Stomee 			fireConsumerStopped(new ConsumerEvent(this,
793127bbe13Stomee 				System.nanoTime()));
794fb3fb4f3Stomee 		    } catch (DTraceException e) {
795fb3fb4f3Stomee 			if (exceptionHandler != null) {
796fb3fb4f3Stomee 			    exceptionHandler.handleException(e);
797fb3fb4f3Stomee 			} else {
798fb3fb4f3Stomee 			    e.printStackTrace();
799fb3fb4f3Stomee 			}
800fb3fb4f3Stomee 		    }
801fb3fb4f3Stomee 		    break;
802fb3fb4f3Stomee 		case STARTED:
80352aacb45Stomee 		    running = true;
804fb3fb4f3Stomee 		    break;
805fb3fb4f3Stomee 		case STOPPED:
806fb3fb4f3Stomee 		    //
80752aacb45Stomee 		    // The work() thread that runs the native consumer
80852aacb45Stomee 		    // loop may have terminated because of the exit()
80952aacb45Stomee 		    // action in a DTrace program.  In that case, a
81052aacb45Stomee 		    // RuntimeException is inappropriate because there
81152aacb45Stomee 		    // is no misuse of the API.  Creating a new checked
81252aacb45Stomee 		    // exception type to handle this case seems to offer
81352aacb45Stomee 		    // no benefit for the trouble to the caller.
81452aacb45Stomee 		    // Instead, the situation calls for stop() to be
81552aacb45Stomee 		    // quietly tolerant.
816fb3fb4f3Stomee 		    //
817fb3fb4f3Stomee 		    if (stopCalled) {
81852aacb45Stomee 			throw new IllegalStateException(
81952aacb45Stomee 				"consumer already stopped");
820fb3fb4f3Stomee 		    }
821382dbd46Stomee 		    logger.fine("consumer already stopped");
822fb3fb4f3Stomee 		    break;
823fb3fb4f3Stomee 		case CLOSED:
824fb3fb4f3Stomee 		    throw new IllegalStateException("consumer closed");
825fb3fb4f3Stomee 		default:
82652aacb45Stomee 		    throw new IllegalArgumentException("unknown state: " +
82752aacb45Stomee 			    state);
828fb3fb4f3Stomee 	    }
829fb3fb4f3Stomee 
830fb3fb4f3Stomee 	    stopCalled = true;
831fb3fb4f3Stomee 	}
832fb3fb4f3Stomee 
83352aacb45Stomee 	if (running) {
834382dbd46Stomee 	    if (Thread.holdsLock(this)) {
835382dbd46Stomee 		throw new IllegalThreadStateException("The current " +
836382dbd46Stomee 			"thread cannot stop this LocalConsumer while " +
837382dbd46Stomee 			"holding the lock on this LocalConsumer");
838382dbd46Stomee 	    }
839382dbd46Stomee 
84052aacb45Stomee 	    //
84152aacb45Stomee 	    // Calls no libdtrace methods, so no synchronization is
84252aacb45Stomee 	    // needed.  Sets a native flag that causes the consumer
84352aacb45Stomee 	    // thread to exit the consumer loop and call native
84452aacb45Stomee 	    // dtrace_stop() at the end of the current interval (after
84552aacb45Stomee 	    // grabbing the global Consumer.class lock required for any
84652aacb45Stomee 	    // libdtrace call).
84752aacb45Stomee 	    //
84852aacb45Stomee 	    _interrupt();
84952aacb45Stomee 
85052aacb45Stomee 	    synchronized (stopLock) {
85152aacb45Stomee 		//
85252aacb45Stomee 		// Wait for work() to set workEnded.  If the work()
85352aacb45Stomee 		// thread got the stopLock first, then workEnded is
85452aacb45Stomee 		// already set.
85552aacb45Stomee 		//
85652aacb45Stomee 		while (!workEnded) {
85752aacb45Stomee 		    try {
85852aacb45Stomee 			stopLock.wait();
85952aacb45Stomee 		    } catch (InterruptedException e) {
86052aacb45Stomee 			logger.warning(e.toString());
86152aacb45Stomee 			// do nothing but re-check the condition for
86252aacb45Stomee 			// waiting
86352aacb45Stomee 		    }
86452aacb45Stomee 		}
86552aacb45Stomee 	    }
86652aacb45Stomee 	}
86752aacb45Stomee     }
86852aacb45Stomee 
86943fb4b48Stomee     public synchronized void
abort()870382dbd46Stomee     abort()
871382dbd46Stomee     {
87243fb4b48Stomee 	if ((state != State.INIT) && (state != State.CLOSED)) {
873382dbd46Stomee 	    _interrupt();
874382dbd46Stomee 	}
87543fb4b48Stomee 	abortCalled = true;
87643fb4b48Stomee     }
877382dbd46Stomee 
878382dbd46Stomee     /**
879*3a931819SPeter Tribble      * {@inheritDoc}
880382dbd46Stomee      *
881382dbd46Stomee      * @throws IllegalThreadStateException if attempting to {@code
882382dbd46Stomee      * close()} a running consumer while holding the lock on that
883382dbd46Stomee      * consumer
884382dbd46Stomee      */
885382dbd46Stomee     public void
close()886fb3fb4f3Stomee     close()
887fb3fb4f3Stomee     {
888382dbd46Stomee 	synchronized (this) {
889fb3fb4f3Stomee 	    if ((state == State.INIT) || (state == State.CLOSED)) {
890fb3fb4f3Stomee 		state = State.CLOSED;
891fb3fb4f3Stomee 		return;
892fb3fb4f3Stomee 	    }
893fb3fb4f3Stomee 	}
894fb3fb4f3Stomee 
895382dbd46Stomee 	try {
896382dbd46Stomee 	    stop();
897382dbd46Stomee 	} catch (IllegalStateException e) {
898382dbd46Stomee 	    // ignore (we don't have synchronized state access because
899382dbd46Stomee 	    // it is illegal to call stop() while holding the lock on
900382dbd46Stomee 	    // this consumer)
901382dbd46Stomee 	}
902382dbd46Stomee 
903382dbd46Stomee 	synchronized (this) {
904382dbd46Stomee 	    if (state != State.CLOSED) {
905fb3fb4f3Stomee 		synchronized (LocalConsumer.class) {
906fb3fb4f3Stomee 		    _close();
907fb3fb4f3Stomee 		}
908fb3fb4f3Stomee 		_destroy();
909fb3fb4f3Stomee 		state = State.CLOSED;
910fb3fb4f3Stomee 
911fb3fb4f3Stomee 		if (logger.isLoggable(Level.INFO)) {
912fb3fb4f3Stomee 		    logger.info("consumer table count: " + _openCount());
913fb3fb4f3Stomee 		}
914fb3fb4f3Stomee 	    }
915382dbd46Stomee 	}
916382dbd46Stomee     }
917fb3fb4f3Stomee 
918fb3fb4f3Stomee     public void
addConsumerListener(ConsumerListener l)919fb3fb4f3Stomee     addConsumerListener(ConsumerListener l)
920fb3fb4f3Stomee     {
921fb3fb4f3Stomee         listenerList.add(ConsumerListener.class, l);
922fb3fb4f3Stomee     }
923fb3fb4f3Stomee 
924fb3fb4f3Stomee     public void
removeConsumerListener(ConsumerListener l)925fb3fb4f3Stomee     removeConsumerListener(ConsumerListener l)
926fb3fb4f3Stomee     {
927fb3fb4f3Stomee         listenerList.remove(ConsumerListener.class, l);
928fb3fb4f3Stomee     }
929fb3fb4f3Stomee 
930fb3fb4f3Stomee     public Aggregate
getAggregate()931fb3fb4f3Stomee     getAggregate() throws DTraceException
932fb3fb4f3Stomee     {
933fb3fb4f3Stomee 	// include all, clear none
934fb3fb4f3Stomee 	return getAggregate(null, Collections. <String> emptySet());
935fb3fb4f3Stomee     }
936fb3fb4f3Stomee 
937fb3fb4f3Stomee     public Aggregate
getAggregate(Set <String> includedAggregationNames)938fb3fb4f3Stomee     getAggregate(Set <String> includedAggregationNames)
939fb3fb4f3Stomee             throws DTraceException
940fb3fb4f3Stomee     {
941fb3fb4f3Stomee 	return getAggregate(includedAggregationNames,
942fb3fb4f3Stomee 		Collections. <String> emptySet());
943fb3fb4f3Stomee     }
944fb3fb4f3Stomee 
945fb3fb4f3Stomee     public Aggregate
getAggregate(Set <String> includedAggregationNames, Set <String> clearedAggregationNames)946fb3fb4f3Stomee     getAggregate(Set <String> includedAggregationNames,
947fb3fb4f3Stomee 	    Set <String> clearedAggregationNames)
948fb3fb4f3Stomee             throws DTraceException
949fb3fb4f3Stomee     {
950fb3fb4f3Stomee 	AggregateSpec spec = new AggregateSpec();
951fb3fb4f3Stomee 
952fb3fb4f3Stomee 	if (includedAggregationNames == null) {
953fb3fb4f3Stomee 	    spec.setIncludeByDefault(true);
954fb3fb4f3Stomee 	} else {
955fb3fb4f3Stomee 	    spec.setIncludeByDefault(false);
956fb3fb4f3Stomee 	    for (String included : includedAggregationNames) {
957fb3fb4f3Stomee 		spec.addIncludedAggregationName(included);
958fb3fb4f3Stomee 	    }
959fb3fb4f3Stomee 	}
960fb3fb4f3Stomee 
961fb3fb4f3Stomee 	if (clearedAggregationNames == null) {
962fb3fb4f3Stomee 	    spec.setClearByDefault(true);
963fb3fb4f3Stomee 	} else {
964fb3fb4f3Stomee 	    spec.setClearByDefault(false);
965fb3fb4f3Stomee 	    for (String cleared : clearedAggregationNames) {
966fb3fb4f3Stomee 		spec.addClearedAggregationName(cleared);
967fb3fb4f3Stomee 	    }
968fb3fb4f3Stomee 	}
969fb3fb4f3Stomee 
970fb3fb4f3Stomee 	return getAggregate(spec);
971fb3fb4f3Stomee     }
972fb3fb4f3Stomee 
973fb3fb4f3Stomee     private synchronized Aggregate
getAggregate(AggregateSpec spec)974fb3fb4f3Stomee     getAggregate(AggregateSpec spec) throws DTraceException
975fb3fb4f3Stomee     {
976fb3fb4f3Stomee 	//
977fb3fb4f3Stomee 	// It should be possible to request aggregation data after a
978fb3fb4f3Stomee 	// consumer has stopped but not after it has been closed.
979fb3fb4f3Stomee 	//
980fb3fb4f3Stomee 	checkGoCalled();
981fb3fb4f3Stomee 
982fb3fb4f3Stomee 	//
983fb3fb4f3Stomee 	// Getting the aggregate is a time-consuming request that should not
984fb3fb4f3Stomee 	// prevent other consumers from running concurrently.  Instead,
985fb3fb4f3Stomee 	// native code will acquire the LocalConsumer.class monitor as
986fb3fb4f3Stomee 	// needed before calling libdtrace functions.
987fb3fb4f3Stomee 	//
988fb3fb4f3Stomee 	Aggregate aggregate = _getAggregate(spec);
989fb3fb4f3Stomee 	return aggregate;
990fb3fb4f3Stomee     }
991fb3fb4f3Stomee 
992fb3fb4f3Stomee     private synchronized void
checkGoCalled()993fb3fb4f3Stomee     checkGoCalled()
994fb3fb4f3Stomee     {
995fb3fb4f3Stomee 	switch (state) {
996fb3fb4f3Stomee 	    case INIT:
997fb3fb4f3Stomee 		throw new IllegalStateException("consumer not open");
998fb3fb4f3Stomee 	    case OPEN:
999fb3fb4f3Stomee 	    case COMPILED:
1000fb3fb4f3Stomee 		throw new IllegalStateException("go() not called");
1001fb3fb4f3Stomee 	    case GO:
1002fb3fb4f3Stomee 	    case STARTED:
1003fb3fb4f3Stomee 	    case STOPPED:
1004fb3fb4f3Stomee 		break;
1005fb3fb4f3Stomee 	    case CLOSED:
1006fb3fb4f3Stomee 		throw new IllegalStateException("consumer closed");
1007fb3fb4f3Stomee 	}
1008fb3fb4f3Stomee     }
1009fb3fb4f3Stomee 
1010fb3fb4f3Stomee     private synchronized void
checkGoNotCalled()1011fb3fb4f3Stomee     checkGoNotCalled()
1012fb3fb4f3Stomee     {
1013fb3fb4f3Stomee 	switch (state) {
1014fb3fb4f3Stomee 	    case INIT:
1015fb3fb4f3Stomee 		throw new IllegalStateException("consumer not open");
1016fb3fb4f3Stomee 	    case OPEN:
1017fb3fb4f3Stomee 	    case COMPILED:
1018fb3fb4f3Stomee 		break;
1019fb3fb4f3Stomee 	    case GO:
1020fb3fb4f3Stomee 	    case STARTED:
1021fb3fb4f3Stomee 		throw new IllegalStateException("go() already called");
1022fb3fb4f3Stomee 	    case STOPPED:
1023fb3fb4f3Stomee 		throw new IllegalStateException("consumer stopped");
1024fb3fb4f3Stomee 	    case CLOSED:
1025fb3fb4f3Stomee 		throw new IllegalStateException("consumer closed");
1026fb3fb4f3Stomee 	}
1027fb3fb4f3Stomee     }
1028fb3fb4f3Stomee 
1029fb3fb4f3Stomee     public synchronized int
createProcess(String command)1030fb3fb4f3Stomee     createProcess(String command) throws DTraceException
1031fb3fb4f3Stomee     {
1032fb3fb4f3Stomee 	if (command == null) {
1033fb3fb4f3Stomee 	    throw new NullPointerException("command is null");
1034fb3fb4f3Stomee 	}
1035fb3fb4f3Stomee 
1036fb3fb4f3Stomee 	checkGoNotCalled();
1037fb3fb4f3Stomee 
1038fb3fb4f3Stomee 	int pid;
1039fb3fb4f3Stomee 	synchronized (LocalConsumer.class) {
1040fb3fb4f3Stomee 	    pid = _createProcess(command);
1041fb3fb4f3Stomee 	}
1042fb3fb4f3Stomee 	return pid;
1043fb3fb4f3Stomee     }
1044fb3fb4f3Stomee 
1045fb3fb4f3Stomee     public synchronized void
grabProcess(int pid)1046fb3fb4f3Stomee     grabProcess(int pid) throws DTraceException
1047fb3fb4f3Stomee     {
1048fb3fb4f3Stomee 	checkGoNotCalled();
1049fb3fb4f3Stomee 
1050fb3fb4f3Stomee 	synchronized (LocalConsumer.class) {
1051fb3fb4f3Stomee 	    _grabProcess(pid);
1052fb3fb4f3Stomee 	}
1053fb3fb4f3Stomee     }
1054fb3fb4f3Stomee 
1055fb3fb4f3Stomee     public synchronized List <ProbeDescription>
listProbes(ProbeDescription filter)1056fb3fb4f3Stomee     listProbes(ProbeDescription filter) throws DTraceException
1057fb3fb4f3Stomee     {
1058fb3fb4f3Stomee 	checkGoNotCalled();
1059fb3fb4f3Stomee 	List <ProbeDescription> probeList =
1060fb3fb4f3Stomee 		new LinkedList <ProbeDescription> ();
1061fb3fb4f3Stomee 	if (filter == ProbeDescription.EMPTY) {
1062fb3fb4f3Stomee 	    filter = null;
1063fb3fb4f3Stomee 	}
1064fb3fb4f3Stomee 	synchronized (LocalConsumer.class) {
1065fb3fb4f3Stomee 	    _listProbes(probeList, filter);
1066fb3fb4f3Stomee 	}
1067fb3fb4f3Stomee 	return probeList;
1068fb3fb4f3Stomee     }
1069fb3fb4f3Stomee 
1070fb3fb4f3Stomee     public synchronized List <Probe>
listProbeDetail(ProbeDescription filter)1071fb3fb4f3Stomee     listProbeDetail(ProbeDescription filter) throws DTraceException
1072fb3fb4f3Stomee     {
1073fb3fb4f3Stomee 	checkGoNotCalled();
1074fb3fb4f3Stomee 	List <Probe> probeList = new LinkedList <Probe> ();
1075fb3fb4f3Stomee 	if (filter == ProbeDescription.EMPTY) {
1076fb3fb4f3Stomee 	    filter = null;
1077fb3fb4f3Stomee 	}
1078fb3fb4f3Stomee 	synchronized (LocalConsumer.class) {
1079fb3fb4f3Stomee 	    _listProbeDetail(probeList, filter);
1080fb3fb4f3Stomee 	}
1081fb3fb4f3Stomee 	return probeList;
1082fb3fb4f3Stomee     }
1083fb3fb4f3Stomee 
1084fb3fb4f3Stomee     public synchronized List <ProbeDescription>
listProgramProbes(Program program)1085fb3fb4f3Stomee     listProgramProbes(Program program) throws DTraceException
1086fb3fb4f3Stomee     {
1087fb3fb4f3Stomee 	checkProgram(program);
1088fb3fb4f3Stomee 	checkGoNotCalled();
1089fb3fb4f3Stomee 	List <ProbeDescription> probeList =
1090fb3fb4f3Stomee 		new LinkedList <ProbeDescription> ();
1091fb3fb4f3Stomee 	synchronized (LocalConsumer.class) {
1092fb3fb4f3Stomee 	    _listCompiledProbes(probeList, program);
1093fb3fb4f3Stomee 	}
1094fb3fb4f3Stomee 	return probeList;
1095fb3fb4f3Stomee     }
1096fb3fb4f3Stomee 
1097fb3fb4f3Stomee     public synchronized List <Probe>
listProgramProbeDetail(Program program)1098fb3fb4f3Stomee     listProgramProbeDetail(Program program) throws DTraceException
1099fb3fb4f3Stomee     {
1100fb3fb4f3Stomee 	checkProgram(program);
1101fb3fb4f3Stomee 	checkGoNotCalled();
1102fb3fb4f3Stomee 	List <Probe> probeList = new LinkedList <Probe> ();
1103fb3fb4f3Stomee 	synchronized (LocalConsumer.class) {
1104fb3fb4f3Stomee 	    _listCompiledProbeDetail(probeList, program);
1105fb3fb4f3Stomee 	}
1106fb3fb4f3Stomee 	return probeList;
1107fb3fb4f3Stomee     }
1108fb3fb4f3Stomee 
1109fb3fb4f3Stomee     public synchronized String
lookupKernelFunction(int address)1110fb3fb4f3Stomee     lookupKernelFunction(int address)
1111fb3fb4f3Stomee     {
1112fb3fb4f3Stomee 	checkGoCalled();
1113fb3fb4f3Stomee 	synchronized (LocalConsumer.class) {
1114fb3fb4f3Stomee 	    return _lookupKernelFunction(new Integer(address));
1115fb3fb4f3Stomee 	}
1116fb3fb4f3Stomee     }
1117fb3fb4f3Stomee 
1118fb3fb4f3Stomee     public synchronized String
lookupKernelFunction(long address)1119fb3fb4f3Stomee     lookupKernelFunction(long address)
1120fb3fb4f3Stomee     {
1121fb3fb4f3Stomee 	checkGoCalled();
1122fb3fb4f3Stomee 	synchronized (LocalConsumer.class) {
1123fb3fb4f3Stomee 	    return _lookupKernelFunction(new Long(address));
1124fb3fb4f3Stomee 	}
1125fb3fb4f3Stomee     }
1126fb3fb4f3Stomee 
1127fb3fb4f3Stomee     public synchronized String
lookupUserFunction(int pid, int address)1128fb3fb4f3Stomee     lookupUserFunction(int pid, int address)
1129fb3fb4f3Stomee     {
1130fb3fb4f3Stomee 	checkGoCalled();
1131fb3fb4f3Stomee 	synchronized (LocalConsumer.class) {
1132fb3fb4f3Stomee 	    return _lookupUserFunction(pid, new Integer(address));
1133fb3fb4f3Stomee 	}
1134fb3fb4f3Stomee     }
1135fb3fb4f3Stomee 
1136fb3fb4f3Stomee     public synchronized String
lookupUserFunction(int pid, long address)1137fb3fb4f3Stomee     lookupUserFunction(int pid, long address)
1138fb3fb4f3Stomee     {
1139fb3fb4f3Stomee 	checkGoCalled();
1140fb3fb4f3Stomee 	synchronized (LocalConsumer.class) {
1141fb3fb4f3Stomee 	    return _lookupUserFunction(pid, new Long(address));
1142fb3fb4f3Stomee 	}
1143fb3fb4f3Stomee     }
1144fb3fb4f3Stomee 
1145fb3fb4f3Stomee     public String
getVersion()1146fb3fb4f3Stomee     getVersion()
1147fb3fb4f3Stomee     {
1148fb3fb4f3Stomee 	synchronized (LocalConsumer.class) {
1149fb3fb4f3Stomee 	    return LocalConsumer._getVersion();
1150fb3fb4f3Stomee 	}
1151fb3fb4f3Stomee     }
1152fb3fb4f3Stomee 
1153fb3fb4f3Stomee     /**
1154fb3fb4f3Stomee      * Called by native code.
1155fb3fb4f3Stomee      */
1156fb3fb4f3Stomee     private void
nextProbeData(ProbeData probeData)1157fb3fb4f3Stomee     nextProbeData(ProbeData probeData) throws ConsumerException
1158fb3fb4f3Stomee     {
1159fb3fb4f3Stomee 	fireDataReceived(new DataEvent(this, probeData));
1160fb3fb4f3Stomee     }
1161fb3fb4f3Stomee 
1162fb3fb4f3Stomee     /**
1163fb3fb4f3Stomee      * Called by native code.
1164fb3fb4f3Stomee      */
1165fb3fb4f3Stomee     private void
dataDropped(Drop drop)1166fb3fb4f3Stomee     dataDropped(Drop drop) throws ConsumerException
1167fb3fb4f3Stomee     {
1168fb3fb4f3Stomee 	fireDataDropped(new DropEvent(this, drop));
1169fb3fb4f3Stomee     }
1170fb3fb4f3Stomee 
1171fb3fb4f3Stomee     /**
1172fb3fb4f3Stomee      * Called by native code.
1173fb3fb4f3Stomee      */
1174fb3fb4f3Stomee     private void
errorEncountered(Error error)1175fb3fb4f3Stomee     errorEncountered(Error error) throws ConsumerException
1176fb3fb4f3Stomee     {
1177fb3fb4f3Stomee 	fireErrorEncountered(new ErrorEvent(this, error));
1178fb3fb4f3Stomee     }
1179fb3fb4f3Stomee 
1180fb3fb4f3Stomee     /**
1181fb3fb4f3Stomee      * Called by native code.
1182fb3fb4f3Stomee      */
1183fb3fb4f3Stomee     private void
processStateChanged(ProcessState processState)1184fb3fb4f3Stomee     processStateChanged(ProcessState processState) throws ConsumerException
1185fb3fb4f3Stomee     {
1186fb3fb4f3Stomee 	fireProcessStateChanged(new ProcessEvent(this, processState));
1187fb3fb4f3Stomee     }
1188fb3fb4f3Stomee 
1189fb3fb4f3Stomee     protected void
fireDataReceived(DataEvent e)1190fb3fb4f3Stomee     fireDataReceived(DataEvent e) throws ConsumerException
1191fb3fb4f3Stomee     {
1192fb3fb4f3Stomee         // Guaranteed to return a non-null array
1193fb3fb4f3Stomee         Object[] listeners = listenerList.getListenerList();
1194fb3fb4f3Stomee         // Process the listeners last to first, notifying
1195fb3fb4f3Stomee         // those that are interested in this event
1196fb3fb4f3Stomee         for (int i = listeners.length - 2; i >= 0; i -= 2) {
1197fb3fb4f3Stomee             if (listeners[i] == ConsumerListener.class) {
1198fb3fb4f3Stomee                 ((ConsumerListener)listeners[i + 1]).dataReceived(e);
1199fb3fb4f3Stomee             }
1200fb3fb4f3Stomee         }
1201fb3fb4f3Stomee     }
1202fb3fb4f3Stomee 
1203fb3fb4f3Stomee     protected void
fireDataDropped(DropEvent e)1204fb3fb4f3Stomee     fireDataDropped(DropEvent e) throws ConsumerException
1205fb3fb4f3Stomee     {
1206fb3fb4f3Stomee         // Guaranteed to return a non-null array
1207fb3fb4f3Stomee         Object[] listeners = listenerList.getListenerList();
1208fb3fb4f3Stomee         // Process the listeners last to first, notifying
1209fb3fb4f3Stomee         // those that are interested in this event
1210fb3fb4f3Stomee         for (int i = listeners.length - 2; i >= 0; i -= 2) {
1211fb3fb4f3Stomee             if (listeners[i] == ConsumerListener.class) {
1212fb3fb4f3Stomee                 ((ConsumerListener)listeners[i + 1]).dataDropped(e);
1213fb3fb4f3Stomee             }
1214fb3fb4f3Stomee         }
1215fb3fb4f3Stomee     }
1216fb3fb4f3Stomee 
1217fb3fb4f3Stomee     protected void
fireErrorEncountered(ErrorEvent e)1218fb3fb4f3Stomee     fireErrorEncountered(ErrorEvent e) throws ConsumerException
1219fb3fb4f3Stomee     {
1220fb3fb4f3Stomee         // Guaranteed to return a non-null array
1221fb3fb4f3Stomee         Object[] listeners = listenerList.getListenerList();
1222fb3fb4f3Stomee         // Process the listeners last to first, notifying
1223fb3fb4f3Stomee         // those that are interested in this event
1224fb3fb4f3Stomee         for (int i = listeners.length - 2; i >= 0; i -= 2) {
1225fb3fb4f3Stomee             if (listeners[i] == ConsumerListener.class) {
1226fb3fb4f3Stomee                 ((ConsumerListener)listeners[i + 1]).errorEncountered(e);
1227fb3fb4f3Stomee             }
1228fb3fb4f3Stomee         }
1229fb3fb4f3Stomee     }
1230fb3fb4f3Stomee 
1231fb3fb4f3Stomee     protected void
fireProcessStateChanged(ProcessEvent e)1232fb3fb4f3Stomee     fireProcessStateChanged(ProcessEvent e) throws ConsumerException
1233fb3fb4f3Stomee     {
1234fb3fb4f3Stomee         // Guaranteed to return a non-null array
1235fb3fb4f3Stomee         Object[] listeners = listenerList.getListenerList();
1236fb3fb4f3Stomee         // Process the listeners last to first, notifying
1237fb3fb4f3Stomee         // those that are interested in this event
1238fb3fb4f3Stomee         for (int i = listeners.length - 2; i >= 0; i -= 2) {
1239fb3fb4f3Stomee             if (listeners[i] == ConsumerListener.class) {
1240fb3fb4f3Stomee                 ((ConsumerListener)listeners[i + 1]).processStateChanged(e);
1241fb3fb4f3Stomee             }
1242fb3fb4f3Stomee         }
1243fb3fb4f3Stomee     }
1244fb3fb4f3Stomee 
1245fb3fb4f3Stomee     protected void
fireConsumerStarted(ConsumerEvent e)1246fb3fb4f3Stomee     fireConsumerStarted(ConsumerEvent e)
1247fb3fb4f3Stomee     {
1248fb3fb4f3Stomee         // Guaranteed to return a non-null array
1249fb3fb4f3Stomee         Object[] listeners = listenerList.getListenerList();
1250fb3fb4f3Stomee         // Process the listeners last to first, notifying
1251fb3fb4f3Stomee         // those that are interested in this event
1252fb3fb4f3Stomee         for (int i = listeners.length - 2; i >= 0; i -= 2) {
1253fb3fb4f3Stomee             if (listeners[i] == ConsumerListener.class) {
1254fb3fb4f3Stomee                 ((ConsumerListener)listeners[i + 1]).consumerStarted(e);
1255fb3fb4f3Stomee             }
1256fb3fb4f3Stomee         }
1257fb3fb4f3Stomee     }
1258fb3fb4f3Stomee 
1259fb3fb4f3Stomee     protected void
fireConsumerStopped(ConsumerEvent e)1260fb3fb4f3Stomee     fireConsumerStopped(ConsumerEvent e)
1261fb3fb4f3Stomee     {
1262fb3fb4f3Stomee         // Guaranteed to return a non-null array
1263fb3fb4f3Stomee         Object[] listeners = listenerList.getListenerList();
1264fb3fb4f3Stomee         // Process the listeners last to first, notifying
1265fb3fb4f3Stomee         // those that are interested in this event
1266fb3fb4f3Stomee         for (int i = listeners.length - 2; i >= 0; i -= 2) {
1267fb3fb4f3Stomee             if (listeners[i] == ConsumerListener.class) {
1268fb3fb4f3Stomee                 ((ConsumerListener)listeners[i + 1]).consumerStopped(e);
1269fb3fb4f3Stomee             }
1270fb3fb4f3Stomee         }
1271fb3fb4f3Stomee     }
1272fb3fb4f3Stomee 
1273fb3fb4f3Stomee     // Called by native code
1274fb3fb4f3Stomee     private void
intervalBegan()1275fb3fb4f3Stomee     intervalBegan()
1276fb3fb4f3Stomee     {
1277fb3fb4f3Stomee 	fireIntervalBegan(new ConsumerEvent(this, System.nanoTime()));
1278fb3fb4f3Stomee     }
1279fb3fb4f3Stomee 
1280fb3fb4f3Stomee     protected void
fireIntervalBegan(ConsumerEvent e)1281fb3fb4f3Stomee     fireIntervalBegan(ConsumerEvent e)
1282fb3fb4f3Stomee     {
1283fb3fb4f3Stomee         // Guaranteed to return a non-null array
1284fb3fb4f3Stomee         Object[] listeners = listenerList.getListenerList();
1285fb3fb4f3Stomee         // Process the listeners last to first, notifying
1286fb3fb4f3Stomee         // those that are interested in this event
1287fb3fb4f3Stomee         for (int i = listeners.length - 2; i >= 0; i -= 2) {
1288fb3fb4f3Stomee             if (listeners[i] == ConsumerListener.class) {
1289fb3fb4f3Stomee                 ((ConsumerListener)listeners[i + 1]).intervalBegan(e);
1290fb3fb4f3Stomee             }
1291fb3fb4f3Stomee         }
1292fb3fb4f3Stomee     }
1293fb3fb4f3Stomee 
1294fb3fb4f3Stomee     // Called by native code
1295fb3fb4f3Stomee     private void
intervalEnded()1296fb3fb4f3Stomee     intervalEnded()
1297fb3fb4f3Stomee     {
1298fb3fb4f3Stomee 	fireIntervalEnded(new ConsumerEvent(this, System.nanoTime()));
1299fb3fb4f3Stomee     }
1300fb3fb4f3Stomee 
1301fb3fb4f3Stomee     protected void
fireIntervalEnded(ConsumerEvent e)1302fb3fb4f3Stomee     fireIntervalEnded(ConsumerEvent e)
1303fb3fb4f3Stomee     {
1304fb3fb4f3Stomee         // Guaranteed to return a non-null array
1305fb3fb4f3Stomee         Object[] listeners = listenerList.getListenerList();
1306fb3fb4f3Stomee         // Process the listeners last to first, notifying
1307fb3fb4f3Stomee         // those that are interested in this event
1308fb3fb4f3Stomee         for (int i = listeners.length - 2; i >= 0; i -= 2) {
1309fb3fb4f3Stomee             if (listeners[i] == ConsumerListener.class) {
1310fb3fb4f3Stomee                 ((ConsumerListener)listeners[i + 1]).intervalEnded(e);
1311fb3fb4f3Stomee             }
1312fb3fb4f3Stomee         }
1313fb3fb4f3Stomee     }
1314fb3fb4f3Stomee 
1315fb3fb4f3Stomee     /**
1316fb3fb4f3Stomee      * Gets a string representation of this consumer useful for logging
1317fb3fb4f3Stomee      * and not intended for display.  The exact details of the
1318fb3fb4f3Stomee      * representation are unspecified and subject to change, but the
1319fb3fb4f3Stomee      * following format may be regarded as typical:
1320fb3fb4f3Stomee      * <pre><code>
1321fb3fb4f3Stomee      * class-name[property1 = value1, property2 = value2]
1322fb3fb4f3Stomee      * </code></pre>
1323fb3fb4f3Stomee      */
1324fb3fb4f3Stomee     public String
toString()1325fb3fb4f3Stomee     toString()
1326fb3fb4f3Stomee     {
13274ae67516Stomee 	StringBuilder buf = new StringBuilder(LocalConsumer.class.getName());
1328fb3fb4f3Stomee 	synchronized (this) {
1329fb3fb4f3Stomee 	    buf.append("[open = ");
1330fb3fb4f3Stomee 	    buf.append(isOpen());
1331fb3fb4f3Stomee 	    buf.append(", enabled = ");
1332fb3fb4f3Stomee 	    buf.append(isEnabled());
1333fb3fb4f3Stomee 	    buf.append(", running = ");
1334fb3fb4f3Stomee 	    buf.append(isRunning());
1335fb3fb4f3Stomee 	    buf.append(", closed = ");
1336fb3fb4f3Stomee 	    buf.append(isClosed());
1337fb3fb4f3Stomee 	}
1338fb3fb4f3Stomee 	buf.append(']');
1339fb3fb4f3Stomee 	return buf.toString();
1340fb3fb4f3Stomee     }
1341fb3fb4f3Stomee 
1342fb3fb4f3Stomee     /**
1343fb3fb4f3Stomee      * Ensures that the {@link #close()} method of this consumer has
1344fb3fb4f3Stomee      * been called before it is garbage-collected.  The intended safety
1345fb3fb4f3Stomee      * net is weak because the JVM does not guarantee that an object
1346fb3fb4f3Stomee      * will be garbage-collected when it is no longer referenced.  Users
1347fb3fb4f3Stomee      * of the API should call {@code close()} to ensure that all
1348fb3fb4f3Stomee      * resources associated with this consumer are reclaimed in a timely
1349fb3fb4f3Stomee      * manner.
1350fb3fb4f3Stomee      *
1351fb3fb4f3Stomee      * @see #close()
1352fb3fb4f3Stomee      */
1353fb3fb4f3Stomee     protected void
finalize()1354fb3fb4f3Stomee     finalize()
1355fb3fb4f3Stomee     {
1356fb3fb4f3Stomee 	close();
1357fb3fb4f3Stomee     }
1358fb3fb4f3Stomee 
1359fb3fb4f3Stomee     private String
getTag()1360fb3fb4f3Stomee     getTag()
1361fb3fb4f3Stomee     {
1362fb3fb4f3Stomee 	return super.toString();
1363fb3fb4f3Stomee     }
1364fb3fb4f3Stomee 
1365fb3fb4f3Stomee     //
1366fb3fb4f3Stomee     // Uniquely identifies a consumer across systems so it is possible
1367fb3fb4f3Stomee     // to validate that an object such as a Program passed to a remote
1368fb3fb4f3Stomee     // client over a socket was created by this consumer and no other.
1369fb3fb4f3Stomee     //
1370fb3fb4f3Stomee     static class Identifier implements Serializable {
1371fb3fb4f3Stomee 	static final long serialVersionUID = 2183165132305302834L;
1372fb3fb4f3Stomee 
1373fb3fb4f3Stomee 	// local identifier
1374fb3fb4f3Stomee 	private int id;
1375fb3fb4f3Stomee 	private long timestamp;
1376fb3fb4f3Stomee 	// remote identifier
1377fb3fb4f3Stomee 	private InetAddress localHost;
1378fb3fb4f3Stomee 	private String tag; // in case localHost not available
1379fb3fb4f3Stomee 
1380fb3fb4f3Stomee 	private
Identifier(LocalConsumer consumer)1381fb3fb4f3Stomee 	Identifier(LocalConsumer consumer)
1382fb3fb4f3Stomee 	{
1383fb3fb4f3Stomee 	    id = LocalConsumer.sequence++;
1384fb3fb4f3Stomee 	    timestamp = System.currentTimeMillis();
1385fb3fb4f3Stomee 	    try {
1386fb3fb4f3Stomee 		localHost = InetAddress.getLocalHost();
1387fb3fb4f3Stomee 	    } catch (UnknownHostException e) {
1388fb3fb4f3Stomee 		localHost = null;
1389fb3fb4f3Stomee 	    }
1390fb3fb4f3Stomee 	    tag = consumer.getTag();
1391fb3fb4f3Stomee 	}
1392fb3fb4f3Stomee 
1393fb3fb4f3Stomee 	@Override
1394fb3fb4f3Stomee 	public boolean
equals(Object o)1395fb3fb4f3Stomee 	equals(Object o)
1396fb3fb4f3Stomee 	{
1397fb3fb4f3Stomee 	    if (o == this) {
1398fb3fb4f3Stomee 		return true;
1399fb3fb4f3Stomee 	    }
1400fb3fb4f3Stomee 	    if (o instanceof Identifier) {
1401fb3fb4f3Stomee 		Identifier i = (Identifier)o;
1402fb3fb4f3Stomee 		return ((id == i.id) &&
1403fb3fb4f3Stomee 			(timestamp == i.timestamp) &&
1404fb3fb4f3Stomee 			((localHost == null) ? (i.localHost == null) :
1405fb3fb4f3Stomee 			 localHost.equals(i.localHost)) &&
1406fb3fb4f3Stomee 			tag.equals(i.tag));
1407fb3fb4f3Stomee 	    }
1408fb3fb4f3Stomee 	    return false;
1409fb3fb4f3Stomee 	}
1410fb3fb4f3Stomee 
1411fb3fb4f3Stomee 	@Override
1412fb3fb4f3Stomee 	public int
hashCode()1413fb3fb4f3Stomee 	hashCode()
1414fb3fb4f3Stomee 	{
1415fb3fb4f3Stomee 	    int hash = 17;
1416fb3fb4f3Stomee 	    hash = (37 * hash) + id;
1417fb3fb4f3Stomee 	    hash = (37 * hash) + ((int)(timestamp ^ (timestamp >>> 32)));
1418fb3fb4f3Stomee 	    hash = (37 * hash) + (localHost == null ? 0 :
1419fb3fb4f3Stomee 		    localHost.hashCode());
1420fb3fb4f3Stomee 	    hash = (37 * hash) + tag.hashCode();
1421fb3fb4f3Stomee 	    return hash;
1422fb3fb4f3Stomee 	}
1423fb3fb4f3Stomee 
1424fb3fb4f3Stomee 	@Override
1425fb3fb4f3Stomee 	public String
toString()1426fb3fb4f3Stomee 	toString()
1427fb3fb4f3Stomee 	{
14284ae67516Stomee 	    StringBuilder buf = new StringBuilder();
1429fb3fb4f3Stomee 	    buf.append(Identifier.class.getName());
1430fb3fb4f3Stomee 	    buf.append("[id = ");
1431fb3fb4f3Stomee 	    buf.append(id);
1432fb3fb4f3Stomee 	    buf.append(", timestamp = ");
1433fb3fb4f3Stomee 	    buf.append(timestamp);
1434fb3fb4f3Stomee 	    buf.append(", localHost = ");
1435fb3fb4f3Stomee 	    buf.append(localHost);
1436fb3fb4f3Stomee 	    buf.append(", tag = ");
1437fb3fb4f3Stomee 	    buf.append(tag);
1438fb3fb4f3Stomee 	    buf.append(']');
1439fb3fb4f3Stomee 	    return buf.toString();
1440fb3fb4f3Stomee 	}
1441fb3fb4f3Stomee     }
1442fb3fb4f3Stomee }
1443