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