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