kieker.analysis.plugin.reader.timer.TimeReader Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of kieker Show documentation
Show all versions of kieker Show documentation
Kieker: Application Performance Monitoring and Dynamic Software Analysis
/***************************************************************************
* Copyright 2013 Kieker Project (http://kieker-monitoring.net)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
***************************************************************************/
package kieker.analysis.plugin.reader.timer;
import java.util.concurrent.CancellationException;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.ScheduledThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import kieker.analysis.IProjectContext;
import kieker.analysis.plugin.annotation.OutputPort;
import kieker.analysis.plugin.annotation.Plugin;
import kieker.analysis.plugin.annotation.Property;
import kieker.analysis.plugin.reader.AbstractReaderPlugin;
import kieker.common.configuration.Configuration;
import kieker.common.logging.Log;
import kieker.common.logging.LogFactory;
import kieker.common.record.misc.TimestampRecord;
/**
* This plugin provides the current (system) time in regular intervals. The time is delivered to the two output ports as both a timestamp and a
* {@link TimestampRecord} instance.
*
*
* The reader can be configured to emit an arbitrary amount of signals. It can also be configured to emit an infinite amount of signals.
*
*
* The sent timestamps are created using {@link System#nanoTime()} as a time source, which is being converted to the global time unit (as defined in the
* configuration from the given {@link IProjectContext}).
*
* @author Nils Christian Ehmke
*
* @since 1.8
*/
@Plugin(
description = "Delivers the current (system) time in regular intervals",
outputPorts = {
@OutputPort(name = TimeReader.OUTPUT_PORT_NAME_TIMESTAMPS, eventTypes = Long.class),
@OutputPort(name = TimeReader.OUTPUT_PORT_NAME_TIMESTAMP_RECORDS, eventTypes = TimestampRecord.class)
},
configuration = {
@Property(name = TimeReader.CONFIG_PROPERTY_NAME_UPDATE_INTERVAL_NS, defaultValue = TimeReader.CONFIG_PROPERTY_VALUE_UPDATE_INTERVAL_NS,
description = "Determines the update interval in nano seconds."),
@Property(name = TimeReader.CONFIG_PROPERTY_NAME_DELAY_NS, defaultValue = TimeReader.CONFIG_PROPERTY_VALUE_DELAY_NS,
description = "Determines the initial delay in nano seconds."),
@Property(name = TimeReader.CONFIG_PROPERTY_NAME_NUMBER_IMPULSES, defaultValue = TimeReader.CONFIG_PROPERTY_VALUE_NUMBER_IMPULSES,
description = "Determines the number of impulses to emit (0 = infinite).")
})
public final class TimeReader extends AbstractReaderPlugin {
/** The name of the output port for the timestamps. */
public static final String OUTPUT_PORT_NAME_TIMESTAMPS = "timestamps";
/** The name of the output port for the timestamp records. */
public static final String OUTPUT_PORT_NAME_TIMESTAMP_RECORDS = "timestampRecords";
/** The name of the property determining the update interval in nanoseconds. */
public static final String CONFIG_PROPERTY_NAME_UPDATE_INTERVAL_NS = "updateIntervalNS";
/** The default value for the update interval (1 second). */
public static final String CONFIG_PROPERTY_VALUE_UPDATE_INTERVAL_NS = "1000000000";
/** The name of the property determining the initial delay in nanoseconds. */
public static final String CONFIG_PROPERTY_NAME_DELAY_NS = "delayNS";
/** The default value for the initial delay (0 seconds). */
public static final String CONFIG_PROPERTY_VALUE_DELAY_NS = "0";
/** The name of the property determining the number of impulses to emit. */
public static final String CONFIG_PROPERTY_NAME_NUMBER_IMPULSES = "numberImpulses";
/** The default value for number of impulses (infinite). */
public static final String CONFIG_PROPERTY_VALUE_NUMBER_IMPULSES = "0";
/** A value for the number of impulses. It makes sure that the reader emits an infinite amount of signals. */
public static final long INFINITE_EMITS = 0L;
private static final Log LOG = LogFactory.getLog(TimeReader.class);
final CountDownLatch impulseEmitLatch = new CountDownLatch(1); // NOCS NOPMD (package visible)
private volatile boolean terminated;
private final ScheduledExecutorService executorService = new ScheduledThreadPoolExecutor(1);
private volatile ScheduledFuture> result;
private final long initialDelay;
private final long period;
private final long numberImpulses;
private final TimeUnit timeunit;
/**
* Creates a new timer using the given configuration.
*
* @param configuration
* The configuration containing the properties to initialize this timer.
* @param projectContext
* The project context.
*/
public TimeReader(final Configuration configuration, final IProjectContext projectContext) {
super(configuration, projectContext);
this.initialDelay = configuration.getLongProperty(CONFIG_PROPERTY_NAME_DELAY_NS);
this.period = configuration.getLongProperty(CONFIG_PROPERTY_NAME_UPDATE_INTERVAL_NS);
this.numberImpulses = configuration.getLongProperty(CONFIG_PROPERTY_NAME_NUMBER_IMPULSES);
final String recordTimeunitProperty = projectContext.getProperty(IProjectContext.CONFIG_PROPERTY_NAME_RECORDS_TIME_UNIT);
TimeUnit recordTimeunit;
try {
recordTimeunit = TimeUnit.valueOf(recordTimeunitProperty);
} catch (final IllegalArgumentException ex) { // already caught in AnalysisController, should never happen
LOG.warn(recordTimeunitProperty + " is no valid TimeUnit! Using NANOSECONDS instead.");
recordTimeunit = TimeUnit.NANOSECONDS;
}
this.timeunit = recordTimeunit;
}
/**
* {@inheritDoc}
*/
public void terminate(final boolean error) {
if (!this.terminated) {
LOG.info("Shutdown of TimeReader requested.");
this.executorService.shutdown();
try {
this.terminated = this.executorService.awaitTermination(5, TimeUnit.SECONDS);
} catch (final InterruptedException ex) {
// ignore
}
if (!this.terminated) {
// problems shutting down
this.result.cancel(true);
}
}
}
/**
* {@inheritDoc}
*/
public boolean read() {
this.result = this.executorService.scheduleAtFixedRate(new TimestampEventTask(this.numberImpulses), this.initialDelay, this.period, TimeUnit.NANOSECONDS);
try {
if (this.numberImpulses == INFINITE_EMITS) {
this.result.get();
} else {
this.impulseEmitLatch.await();
}
} catch (final ExecutionException ex) {
this.terminate(true);
throw new RuntimeException(ex.getCause()); // NOPMD (throw RunTimeException)
} catch (final InterruptedException ignore) { // NOPMD (ignore exception)
// ignore this one
} catch (final CancellationException ignore) { // NOPMD (ignore exception)
// ignore this one, too
}
this.terminate(false);
return true;
}
@Override
public Configuration getCurrentConfiguration() {
final Configuration configuration = new Configuration();
configuration.setProperty(CONFIG_PROPERTY_NAME_DELAY_NS, Long.toString(this.initialDelay));
configuration.setProperty(CONFIG_PROPERTY_NAME_UPDATE_INTERVAL_NS, Long.toString(this.period));
configuration.setProperty(CONFIG_PROPERTY_NAME_NUMBER_IMPULSES, Long.toString(this.numberImpulses));
return configuration;
}
/**
* Sends the current system time as a new timestamp event.
*/
protected void sendTimestampEvent() {
final long timestamp = this.timeunit.convert(System.nanoTime(), TimeUnit.NANOSECONDS);
super.deliver(OUTPUT_PORT_NAME_TIMESTAMPS, timestamp);
super.deliver(OUTPUT_PORT_NAME_TIMESTAMP_RECORDS, new TimestampRecord(timestamp));
}
/**
* A simple helper class used to send the current system time.
*
* @author Nils Christian Ehmke
*
* @since 1.8
*/
protected class TimestampEventTask implements Runnable {
private final boolean infinite;
private volatile long numberImpulses;
/**
* Creates a new task.
*
* @param numberImpulses
* 0 = infinite
*/
public TimestampEventTask(final long numberImpulses) {
this.numberImpulses = numberImpulses;
if (numberImpulses == 0) {
this.infinite = true;
} else {
this.infinite = false;
}
}
/**
* Executes the task.
*/
public void run() {
if (this.infinite || (this.numberImpulses > 0)) {
TimeReader.this.sendTimestampEvent();
if (!this.infinite && (0 == --this.numberImpulses)) { // NOPMD
TimeReader.this.impulseEmitLatch.countDown();
}
}
}
}
}