/******************************************************************************* * Copyright (c) 2009, 2015 Ericsson * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v1.0 which * accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * Francois Chouinard - Initial API and implementation * Alexandre Montplaisir - Consolidate constructors, merge with TmfDataRequest *******************************************************************************/ package org.eclipse.tracecompass.tmf.core.request; import java.util.concurrent.CountDownLatch; import org.eclipse.jdt.annotation.Nullable; import org.eclipse.tracecompass.internal.tmf.core.TmfCoreTracer; import org.eclipse.tracecompass.tmf.core.event.ITmfEvent; import org.eclipse.tracecompass.tmf.core.filter.ITmfFilter; import org.eclipse.tracecompass.tmf.core.timestamp.TmfTimeRange; /** * TmfEventRequest's are used to obtain series of events from an event provider. * Open ranges can be used, especially for continuous streaming. * <p> * The request is processed asynchronously by a TmfEventProvider and, as events * become available, handleData() is invoked synchronously for each one. * <p> * The TmfEventProvider indicates that the request is completed by calling * done(). The request can be cancelled at any time with cancel(). * <p> * Typical usage: * * <pre> * <code> * TmfEventRequest request = new TmfEventRequest(DataType.class, range, startIndex, nbEvents, priority) { * * public void handleData(ITmfEvent event) { * // do something with the event * } * * public void handleSuccess() { * // callback for when the request completes successfully * } * * public void handleFailure() { * // callback for when the request fails due to an error * } * * public void handleCancel() { * // callback for when the request is cancelled via .cancel() * } * * }; * * eventProvider.sendRequest(request); * </code> * </pre> * * * TODO: Implement request failures (codes, etc...) * * @author Francois Chouinard */ public abstract class TmfEventRequest implements ITmfEventRequest { // ------------------------------------------------------------------------ // Constants // ------------------------------------------------------------------------ private static int fRequestNumber = 0; // ------------------------------------------------------------------------ // Attributes // ------------------------------------------------------------------------ private final Class<? extends ITmfEvent> fDataType; private final ExecutionType fExecType; /** A unique request ID */ private final int fRequestId; /** The requested events time range */ private final TmfTimeRange fRange; /** The index (rank) of the requested event */ protected long fIndex; /** The number of requested events (ALL_DATA for all) */ protected int fNbRequested; /** The number of reads so far */ private int fNbRead; private final CountDownLatch fStartedLatch = new CountDownLatch(1); private final CountDownLatch fCompletedLatch = new CountDownLatch(1); private volatile boolean fRequestFailed = false; private volatile boolean fRequestCanceled = false; private ITmfFilter fEventFilter; private int fDependencyLevel; private @Nullable Throwable fFailureCause; // ------------------------------------------------------------------------ // Constructors // ------------------------------------------------------------------------ /** * Request 'n' events of a given type, for the *whole* trace, at the given * priority. * * @param dataType * The requested data type. * @param index * The index of the first event to retrieve. You can use '0' to * start at the beginning of the trace. * @param nbRequested * The number of events requested. You can use * {@link TmfEventRequest#ALL_DATA} to indicate you want all * events in the trace. * @param priority * The requested execution priority. */ public TmfEventRequest(Class<? extends ITmfEvent> dataType, long index, int nbRequested, ExecutionType priority) { this(dataType, TmfTimeRange.ETERNITY, index, nbRequested, priority); } /** * Request 'n' events of a given type, for the given time range, at the * given priority. * * @param dataType * The requested data type. * @param range * The time range of the requested events. You can use * {@link TmfTimeRange#ETERNITY} to indicate you want to cover * the whole trace. * @param index * The index of the first event to retrieve. You can use '0' to * start at the beginning of the trace. * @param nbRequested * The number of events requested. You can use * {@link TmfEventRequest#ALL_DATA} to indicate you want all * events in the time range. * @param priority * The requested execution priority. */ public TmfEventRequest(Class<? extends ITmfEvent> dataType, TmfTimeRange range, long index, int nbRequested, ExecutionType priority) { this(dataType, range, index, nbRequested, priority, 0); } /** * Request 'n' events of a given type, for the given time range, at the * given priority. * * @param dataType * The requested data type. * @param range * The time range of the requested events. You can use * {@link TmfTimeRange#ETERNITY} to indicate you want to cover * the whole trace. * @param index * The index of the first event to retrieve. You can use '0' to * start at the beginning of the trace. * @param nbRequested * The number of events requested. You can use * {@link TmfEventRequest#ALL_DATA} to indicate you want all * events in the time range. * @param priority * The requested execution priority. * @param dependencyLevel * The dependency level. Use different dependency level for * requests that have a dependency with each other. They will * be serviced separately. * @since 2.0 */ public TmfEventRequest(Class<? extends ITmfEvent> dataType, TmfTimeRange range, long index, int nbRequested, ExecutionType priority, int dependencyLevel) { synchronized (TmfEventRequest.class) { fRequestId = fRequestNumber++; } fDataType = dataType; fIndex = index; fNbRequested = nbRequested; fExecType = priority; fRange = range; fNbRead = 0; fDependencyLevel = dependencyLevel; /* Setup the request tracing if it's enabled */ if (TmfCoreTracer.isRequestTraced()) { String type = getClass().getName(); type = type.substring(type.lastIndexOf('.') + 1); @SuppressWarnings("nls") String message = "CREATED " + (getExecType() == ExecutionType.BACKGROUND ? "(BG)" : "(FG)") + " Type=" + type + " Index=" + getIndex() + " NbReq=" + getNbRequested() + " Range=" + getRange() + " DataType=" + getDataType().getSimpleName() + " DependencyLevel= " + fDependencyLevel; TmfCoreTracer.traceRequest(fRequestId, message); } } // ------------------------------------------------------------------------ // Accessors // ------------------------------------------------------------------------ @Override public int getRequestId() { return fRequestId; } @Override public long getIndex() { return fIndex; } @Override public ExecutionType getExecType() { return fExecType; } @Override public int getNbRequested() { return fNbRequested; } @Override public synchronized int getNbRead() { return fNbRead; } @Override public synchronized boolean isRunning() { return (fStartedLatch.getCount() <= 0 && fCompletedLatch.getCount() > 0); } @Override public synchronized boolean isCompleted() { return (fCompletedLatch.getCount() <= 0); } @Override public synchronized boolean isFailed() { return fRequestFailed; } @Override public synchronized boolean isCancelled() { return fRequestCanceled; } @Override public Class<? extends ITmfEvent> getDataType() { return fDataType; } @Override public TmfTimeRange getRange() { return fRange; } @Override public ITmfFilter getProviderFilter() { return fEventFilter; } @Override public void setProviderFilter(ITmfFilter provider) { fEventFilter = provider; } /** @since 2.0 */ @Override public int getDependencyLevel() { return fDependencyLevel; } /** * @since 2.0 */ @Override public @Nullable Throwable getFailureCause() { return fFailureCause; } // ------------------------------------------------------------------------ // Setters // ------------------------------------------------------------------------ /** * This method is called by the event provider to set the index * corresponding to the time range start time * * @param index * The start time index */ protected void setIndex(int index) { fIndex = index; } @Override public void setStartIndex(int index) { setIndex(index); } // ------------------------------------------------------------------------ // Operators // ------------------------------------------------------------------------ @Override public void handleData(ITmfEvent event) { fNbRead++; } @Override public void handleStarted() { if (TmfCoreTracer.isRequestTraced()) { TmfCoreTracer.traceRequest(getRequestId(), "STARTED"); //$NON-NLS-1$ } } @Override public void handleCompleted() { boolean requestFailed = false; boolean requestCanceled = false; synchronized (this) { requestFailed = fRequestFailed; requestCanceled = fRequestCanceled; } if (requestFailed) { handleFailure(); } else if (requestCanceled) { handleCancel(); } else { handleSuccess(); } if (TmfCoreTracer.isRequestTraced()) { TmfCoreTracer.traceRequest(getRequestId(), "COMPLETED (" + fNbRead + " events read)"); //$NON-NLS-1$ //$NON-NLS-2$ } } @Override public void handleSuccess() { if (TmfCoreTracer.isRequestTraced()) { TmfCoreTracer.traceRequest(getRequestId(), "SUCCEEDED"); //$NON-NLS-1$ } } @Override public void handleFailure() { if (TmfCoreTracer.isRequestTraced()) { TmfCoreTracer.traceRequest(getRequestId(), "FAILED"); //$NON-NLS-1$ } } @Override public void handleCancel() { if (TmfCoreTracer.isRequestTraced()) { TmfCoreTracer.traceRequest(getRequestId(), "CANCELLED"); //$NON-NLS-1$ } } /** * To suspend the client thread until the request starts (or is canceled). * * @throws InterruptedException * If the thread was interrupted while waiting */ public void waitForStart() throws InterruptedException { fStartedLatch.await(); } @Override public void waitForCompletion() throws InterruptedException { fCompletedLatch.await(); } @Override public synchronized void start() { handleStarted(); fStartedLatch.countDown(); } @Override public synchronized void done() { handleCompleted(); fCompletedLatch.countDown(); } /** * @since 2.0 */ @Override public void fail(Exception e) { fRequestFailed = true; fFailureCause = e; done(); } @Override public void cancel() { fRequestCanceled = true; done(); } // ------------------------------------------------------------------------ // Object // ------------------------------------------------------------------------ @Override public String toString() { String name = getClass().getName(); int dot = name.lastIndexOf('.'); if (dot >= 0) { name = name.substring(dot + 1); } return '[' + name + '(' + getRequestId() + ',' + getDataType().getSimpleName() + ',' + getExecType() + ',' + getRange() + ',' + getIndex() + ',' + getNbRequested() + ','+ getDependencyLevel() + ")]"; //$NON-NLS-1$ } }