Remove all existing @since annotations
[deliverable/tracecompass.git] / org.eclipse.tracecompass.tmf.core / src / org / eclipse / tracecompass / tmf / core / analysis / TmfAbstractAnalysisModule.java
1 /*******************************************************************************
2 * Copyright (c) 2013, 2014 École Polytechnique de Montréal
3 *
4 * All rights reserved. This program and the accompanying materials are
5 * made available under the terms of the Eclipse Public License v1.0 which
6 * accompanies this distribution, and is available at
7 * http://www.eclipse.org/legal/epl-v10.html
8 *
9 * Contributors:
10 * Geneviève Bastien - Initial API and implementation
11 *******************************************************************************/
12
13 package org.eclipse.tracecompass.tmf.core.analysis;
14
15 import static org.eclipse.tracecompass.common.core.NonNullUtils.checkNotNull;
16
17 import java.util.ArrayList;
18 import java.util.Collections;
19 import java.util.HashMap;
20 import java.util.List;
21 import java.util.Map;
22 import java.util.Set;
23 import java.util.concurrent.CountDownLatch;
24 import java.util.concurrent.TimeUnit;
25
26 import org.eclipse.core.runtime.IProgressMonitor;
27 import org.eclipse.core.runtime.IStatus;
28 import org.eclipse.core.runtime.NullProgressMonitor;
29 import org.eclipse.core.runtime.Status;
30 import org.eclipse.core.runtime.jobs.Job;
31 import org.eclipse.jdt.annotation.NonNullByDefault;
32 import org.eclipse.jdt.annotation.Nullable;
33 import org.eclipse.osgi.util.NLS;
34 import org.eclipse.tracecompass.common.core.NonNullUtils;
35 import org.eclipse.tracecompass.internal.tmf.core.Activator;
36 import org.eclipse.tracecompass.internal.tmf.core.TmfCoreTracer;
37 import org.eclipse.tracecompass.tmf.core.analysis.TmfAnalysisRequirement.ValuePriorityLevel;
38 import org.eclipse.tracecompass.tmf.core.component.TmfComponent;
39 import org.eclipse.tracecompass.tmf.core.exceptions.TmfAnalysisException;
40 import org.eclipse.tracecompass.tmf.core.signal.TmfSignalHandler;
41 import org.eclipse.tracecompass.tmf.core.signal.TmfStartAnalysisSignal;
42 import org.eclipse.tracecompass.tmf.core.signal.TmfTraceClosedSignal;
43 import org.eclipse.tracecompass.tmf.core.signal.TmfTraceSelectedSignal;
44 import org.eclipse.tracecompass.tmf.core.trace.ITmfTrace;
45 import org.eclipse.tracecompass.tmf.core.trace.TmfTraceManager;
46
47 /**
48 * Base class that analysis modules main class may extend. It provides default
49 * behavior to some methods of the analysis module
50 *
51 * @author Geneviève Bastien
52 */
53 @NonNullByDefault
54 public abstract class TmfAbstractAnalysisModule extends TmfComponent implements IAnalysisModule {
55
56 private @Nullable String fId;
57 private boolean fAutomatic = false, fStarted = false;
58 private volatile @Nullable ITmfTrace fTrace;
59 private final Map<String, Object> fParameters = new HashMap<>();
60 private final List<String> fParameterNames = new ArrayList<>();
61 private final List<IAnalysisOutput> fOutputs = new ArrayList<>();
62 private List<IAnalysisParameterProvider> fParameterProviders = new ArrayList<>();
63 private @Nullable Job fJob = null;
64
65 private final Object syncObj = new Object();
66
67 /* Latch tracking if the analysis is completed or not */
68 private CountDownLatch fFinishedLatch = new CountDownLatch(0);
69
70 private boolean fAnalysisCancelled = false;
71
72 @Override
73 public boolean isAutomatic() {
74 return fAutomatic;
75 }
76
77 @Override
78 public String getName() {
79 return super.getName();
80 }
81
82 @Override
83 public void setName(String name) {
84 super.setName(name);
85 }
86
87 @Override
88 public void setId(String id) {
89 fId = id;
90 }
91
92 @Override
93 public String getId() {
94 String id = fId;
95 if (id == null) {
96 id = this.getClass().getCanonicalName();
97 if (id == null) {
98 /*
99 * Some types, like anonymous classes, don't have a canonical
100 * name. Just use the default name instead.
101 */
102 id = checkNotNull(this.getClass().getName());
103 }
104 fId = id;
105 }
106 return id;
107 }
108
109 @Override
110 public void setAutomatic(boolean auto) {
111 fAutomatic = auto;
112 }
113
114 @Override
115 public void setTrace(ITmfTrace trace) throws TmfAnalysisException {
116 if (fTrace != null) {
117 throw new TmfAnalysisException(NLS.bind(Messages.TmfAbstractAnalysisModule_TraceSetMoreThanOnce, getName()));
118 }
119
120 TmfCoreTracer.traceAnalysis(getId(), trace, "setting trace for analysis"); //$NON-NLS-1$
121
122 /* Check that analysis can be executed */
123 if (!canExecute(trace)) {
124 throw new TmfAnalysisException(NLS.bind(Messages.TmfAbstractAnalysisModule_AnalysisCannotExecute, getName()));
125 }
126
127 fTrace = trace;
128 /* Get the parameter providers for this trace */
129 fParameterProviders = TmfAnalysisManager.getParameterProviders(this, trace);
130 for (IAnalysisParameterProvider provider : fParameterProviders) {
131 TmfCoreTracer.traceAnalysis(getId(), trace, "registered to parameter provider " + provider.getName()); //$NON-NLS-1$
132 provider.registerModule(this);
133 }
134 resetAnalysis();
135 fStarted = false;
136 }
137
138 /**
139 * Gets the trace
140 *
141 * @return The trace
142 */
143 protected @Nullable ITmfTrace getTrace() {
144 return fTrace;
145 }
146
147 @Override
148 public void addParameter(String name) {
149 fParameterNames.add(name);
150 }
151
152 @Override
153 public synchronized void setParameter(String name, @Nullable Object value) {
154 if (!fParameterNames.contains(name)) {
155 throw new RuntimeException(NLS.bind(Messages.TmfAbstractAnalysisModule_InvalidParameter, name, getName()));
156 }
157 Object oldValue = fParameters.get(name);
158 fParameters.put(name, value);
159 if ((value != null) && !(value.equals(oldValue))) {
160 parameterChanged(name);
161 }
162 }
163
164 @Override
165 public synchronized void notifyParameterChanged(String name) {
166 if (!fParameterNames.contains(name)) {
167 throw new RuntimeException(NLS.bind(Messages.TmfAbstractAnalysisModule_InvalidParameter, name, getName()));
168 }
169 Object oldValue = fParameters.get(name);
170 Object value = getParameter(name);
171 if ((value != null) && !(value.equals(oldValue))) {
172 parameterChanged(name);
173 }
174 }
175
176 /**
177 * Used to indicate that a parameter value has been changed
178 *
179 * @param name
180 * The name of the modified parameter
181 */
182 protected void parameterChanged(String name) {
183
184 }
185
186 @Override
187 public @Nullable Object getParameter(String name) {
188 Object paramValue = fParameters.get(name);
189 /* The parameter is not set, maybe it can be provided by someone else */
190 if ((paramValue == null) && (fTrace != null)) {
191 for (IAnalysisParameterProvider provider : fParameterProviders) {
192 paramValue = provider.getParameter(name);
193 if (paramValue != null) {
194 break;
195 }
196 }
197 }
198 return paramValue;
199 }
200
201 @Override
202 public boolean canExecute(ITmfTrace trace) {
203 for (TmfAnalysisRequirement requirement : getAnalysisRequirements()) {
204 if (!requirement.isFulfilled(trace)) {
205 return false;
206 }
207 }
208 return true;
209 }
210
211 /**
212 * Set the countdown latch back to 1 so the analysis can be executed again
213 */
214 protected void resetAnalysis() {
215 TmfCoreTracer.traceAnalysis(getId(), getTrace(), "reset: ready for execution"); //$NON-NLS-1$
216 fFinishedLatch.countDown();
217 fFinishedLatch = new CountDownLatch(1);
218 }
219
220 /**
221 * Actually executes the analysis itself
222 *
223 * @param monitor
224 * Progress monitor
225 * @return Whether the analysis was completed successfully or not
226 * @throws TmfAnalysisException
227 * Method may throw an analysis exception
228 */
229 protected abstract boolean executeAnalysis(final IProgressMonitor monitor) throws TmfAnalysisException;
230
231 /**
232 * Indicate the analysis has been canceled. It is abstract to force
233 * implementing class to cleanup what they are running. This is called by
234 * the job's canceling. It does not need to be called directly.
235 */
236 protected abstract void canceling();
237
238 /**
239 * To be called when the analysis is completed, whether normally or because
240 * it was cancelled or for any other reason.
241 *
242 * It has to be called inside a synchronized block
243 */
244 private void setAnalysisCompleted() {
245 synchronized (syncObj) {
246 fStarted = false;
247 fJob = null;
248 fFinishedLatch.countDown();
249 }
250 }
251
252 /**
253 * Cancels the analysis if it is executing
254 */
255 @Override
256 public final void cancel() {
257 synchronized (syncObj) {
258 TmfCoreTracer.traceAnalysis(getId(), getTrace(), "cancelled by application"); //$NON-NLS-1$
259 if (fJob != null) {
260 if (fJob.cancel()) {
261 fAnalysisCancelled = true;
262 setAnalysisCompleted();
263 }
264 }
265 fStarted = false;
266 }
267 }
268
269 @Override
270 public void dispose() {
271 super.dispose();
272 cancel();
273 }
274
275 /**
276 * Get an iterable list of all analyzes this analysis depends on. These
277 * analyzes will be scheduled before this analysis starts and the current
278 * analysis will not be considered completed until all the dependent
279 * analyzes are finished.
280 *
281 * @return An iterable list of analysis this analyzes depends on.
282 */
283 protected Iterable<IAnalysisModule> getDependentAnalyses() {
284 return checkNotNull(Collections.EMPTY_LIST);
285 }
286
287 private void execute(final ITmfTrace trace) {
288 /*
289 * TODO: The analysis in a job should be done at the analysis manager
290 * level instead of depending on this abstract class implementation,
291 * otherwise another analysis implementation may block the main thread
292 */
293
294 /* Do not execute if analysis has already run */
295 if (fFinishedLatch.getCount() == 0) {
296 TmfCoreTracer.traceAnalysis(getId(), getTrace(), "already executed"); //$NON-NLS-1$
297 return;
298 }
299
300 /* Do not execute if analysis already running */
301 synchronized (syncObj) {
302 if (fStarted) {
303 TmfCoreTracer.traceAnalysis(getId(), getTrace(), "already started, not starting again"); //$NON-NLS-1$
304 return;
305 }
306 fStarted = true;
307 }
308
309 /* Execute dependent analyses before creating the job for this one */
310 final Iterable<IAnalysisModule> dependentAnalyses = getDependentAnalyses();
311 for (IAnalysisModule module : dependentAnalyses) {
312 module.schedule();
313 }
314
315 /*
316 * Actual analysis will be run on a separate thread
317 */
318 String jobName = checkNotNull(NLS.bind(Messages.TmfAbstractAnalysisModule_RunningAnalysis, getName()));
319 fJob = new Job(jobName) {
320 @Override
321 protected @Nullable IStatus run(final @Nullable IProgressMonitor monitor) {
322 IProgressMonitor mon = monitor;
323 if (mon == null) {
324 mon = new NullProgressMonitor();
325 }
326 try {
327 mon.beginTask("", IProgressMonitor.UNKNOWN); //$NON-NLS-1$
328 broadcast(new TmfStartAnalysisSignal(TmfAbstractAnalysisModule.this, TmfAbstractAnalysisModule.this));
329 TmfCoreTracer.traceAnalysis(TmfAbstractAnalysisModule.this.getId(), TmfAbstractAnalysisModule.this.getTrace(), "started"); //$NON-NLS-1$
330 fAnalysisCancelled = !executeAnalysis(mon);
331 for (IAnalysisModule module : dependentAnalyses) {
332 module.waitForCompletion(mon);
333 }
334 TmfCoreTracer.traceAnalysis(TmfAbstractAnalysisModule.this.getId(), TmfAbstractAnalysisModule.this.getTrace(), "finished"); //$NON-NLS-1$
335 } catch (TmfAnalysisException e) {
336 Activator.logError("Error executing analysis with trace " + trace.getName(), e); //$NON-NLS-1$
337 } finally {
338 synchronized (syncObj) {
339 mon.done();
340 setAnalysisCompleted();
341 }
342 TmfTraceManager.refreshSupplementaryFiles(trace);
343 }
344 if (!fAnalysisCancelled) {
345 return Status.OK_STATUS;
346 }
347 // Reset analysis so that it can be executed again.
348 resetAnalysis();
349 return Status.CANCEL_STATUS;
350 }
351
352 @Override
353 protected void canceling() {
354 TmfCoreTracer.traceAnalysis(getId(), getTrace(), "job cancelled"); //$NON-NLS-1$
355 TmfAbstractAnalysisModule.this.canceling();
356 }
357
358 };
359 fJob.schedule();
360 }
361
362 @Override
363 public IStatus schedule() {
364 synchronized (syncObj) {
365 final ITmfTrace trace = getTrace();
366 if (trace == null) {
367 return new Status(IStatus.ERROR, Activator.PLUGIN_ID, String.format("No trace specified for analysis %s", getName())); //$NON-NLS-1$
368 }
369 TmfCoreTracer.traceAnalysis(getId(), getTrace(), "scheduled"); //$NON-NLS-1$
370 execute(trace);
371 }
372
373 return checkNotNull(Status.OK_STATUS);
374 }
375
376 @Override
377 public Iterable<IAnalysisOutput> getOutputs() {
378 return fOutputs;
379 }
380
381 @Override
382 public void registerOutput(IAnalysisOutput output) {
383 if (!fOutputs.contains(output)) {
384 fOutputs.add(output);
385 }
386 }
387
388 @Override
389 public boolean waitForCompletion() {
390 try {
391 fFinishedLatch.await();
392 } catch (InterruptedException e) {
393 Activator.logError("Error while waiting for module completion", e); //$NON-NLS-1$
394 }
395 return !fAnalysisCancelled;
396 }
397
398 @Override
399 public boolean waitForCompletion(IProgressMonitor monitor) {
400 try {
401 while (!fFinishedLatch.await(500, TimeUnit.MILLISECONDS)) {
402 if (fAnalysisCancelled || monitor.isCanceled()) {
403 fAnalysisCancelled = true;
404 return false;
405 }
406 }
407 } catch (InterruptedException e) {
408 Activator.logError("Error while waiting for module completion", e); //$NON-NLS-1$
409 }
410 return !fAnalysisCancelled;
411 }
412
413 /**
414 * Signal handler for trace closing
415 *
416 * @param signal
417 * Trace closed signal
418 */
419 @TmfSignalHandler
420 public void traceClosed(TmfTraceClosedSignal signal) {
421 /* Is the closing trace the one that was requested? */
422 synchronized (syncObj) {
423 if (signal.getTrace() == fTrace) {
424 cancel();
425 fTrace = null;
426 }
427 }
428 }
429
430 /**
431 * Signal handler for when the trace becomes active
432 *
433 * @param signal
434 * Trace selected signal
435 */
436 @TmfSignalHandler
437 public void traceSelected(TmfTraceSelectedSignal signal) {
438 /*
439 * Since some parameter providers may handle many traces, we need to
440 * register the current trace to it
441 */
442 if (signal.getTrace() == fTrace) {
443 for (IAnalysisParameterProvider provider : fParameterProviders) {
444 provider.registerModule(this);
445 }
446 }
447 }
448
449 /**
450 * Returns a full help text to display
451 *
452 * @return Full help text for the module
453 */
454 protected String getFullHelpText() {
455 return NonNullUtils.nullToEmptyString(NLS.bind(
456 Messages.TmfAbstractAnalysisModule_AnalysisModule,
457 getName()));
458 }
459
460 /**
461 * Gets a short help text, to display as header to other help text
462 *
463 * @param trace
464 * The trace to show help for
465 *
466 * @return Short help text describing the module
467 */
468 protected String getShortHelpText(ITmfTrace trace) {
469 return NonNullUtils.nullToEmptyString(NLS.bind(
470 Messages.TmfAbstractAnalysisModule_AnalysisForTrace,
471 getName(), trace.getName()));
472 }
473
474 /**
475 * Gets the help text specific for a trace who does not have required
476 * characteristics for module to execute. The default implementation uses
477 * the analysis requirements.
478 *
479 * @param trace
480 * The trace to apply the analysis to
481 * @return Help text
482 */
483 protected String getTraceCannotExecuteHelpText(ITmfTrace trace) {
484 StringBuilder builder = new StringBuilder();
485 builder.append(NLS.bind(Messages.TmfAbstractAnalysisModule_AnalysisCannotExecute, getName()));
486 for (TmfAnalysisRequirement requirement : getAnalysisRequirements()) {
487 if (!requirement.isFulfilled(trace)) {
488 builder.append("\n\n"); //$NON-NLS-1$
489 builder.append(NLS.bind(Messages.TmfAnalysis_RequirementNotFulfilled, requirement.getType()));
490 builder.append("\n"); //$NON-NLS-1$
491 builder.append(NLS.bind(Messages.TmfAnalysis_RequirementMandatoryValues, requirement.getValues(ValuePriorityLevel.MANDATORY)));
492 Set<String> information = requirement.getInformation();
493 if (!information.isEmpty()) {
494 builder.append("\n"); //$NON-NLS-1$
495 builder.append(NLS.bind(Messages.TmfAnalysis_RequirementInformation, information));
496 }
497 }
498 }
499 return checkNotNull(builder.toString());
500 }
501
502 @Override
503 public String getHelpText() {
504 return getFullHelpText();
505 }
506
507 @Override
508 public String getHelpText(ITmfTrace trace) {
509 String text = getShortHelpText(trace);
510 if (!canExecute(trace)) {
511 text = text + "\n\n" + getTraceCannotExecuteHelpText(trace); //$NON-NLS-1$
512 }
513 return text;
514 }
515
516 @Override
517 public Iterable<TmfAnalysisRequirement> getAnalysisRequirements() {
518 return checkNotNull(Collections.EMPTY_SET);
519 }
520 }
This page took 0.043591 seconds and 5 git commands to generate.