Skip to content
Draft
Show file tree
Hide file tree
Changes from 8 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,282 @@
/**********************************************************************
* Copyright (c) 2025 Ericsson
*
* All rights reserved. This program and the accompanying materials are
* made available under the terms of the Eclipse Public License 2.0 which
* accompanies this distribution, and is available at
* https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
**********************************************************************/
package org.eclipse.tracecompass.tmf.core.config;
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copyright header

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done


import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.io.Writer;
import java.util.ArrayList;
import java.util.List;

import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.Path;
import org.eclipse.jdt.annotation.NonNull;
import org.eclipse.tracecompass.internal.tmf.core.Activator;
import org.eclipse.tracecompass.tmf.core.dataprovider.IDataProviderDescriptor;
import org.eclipse.tracecompass.tmf.core.exceptions.TmfConfigurationException;
import org.eclipse.tracecompass.tmf.core.signal.TmfSignalHandler;
import org.eclipse.tracecompass.tmf.core.signal.TmfSignalManager;
import org.eclipse.tracecompass.tmf.core.signal.TmfTraceClosedSignal;
import org.eclipse.tracecompass.tmf.core.signal.TmfTraceOpenedSignal;
import org.eclipse.tracecompass.tmf.core.trace.ITmfTrace;
import org.eclipse.tracecompass.tmf.core.trace.TmfTraceManager;
import org.eclipse.tracecompass.tmf.core.trace.experiment.TmfExperiment;

import com.google.common.collect.HashBasedTable;
import com.google.common.collect.Table;
import com.google.gson.Gson;
import com.google.gson.JsonParseException;

/**
* This class meant to be extended by data provider factories that want to be
* able to handle configurations.
*
* @since 9.6
*/
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

you need a @since 9.6 tag

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

public abstract class AbstractTmfDataProviderConfigurator implements ITmfDataProviderConfigurator{

/**
* Constructor
*/
protected AbstractTmfDataProviderConfigurator() {
TmfSignalManager.register(this);
}

/**
* The json file extension
* @since 9.5
*/
public static final String JSON_EXTENSION = "json"; //$NON-NLS-1$

private Table<String, ITmfTrace, ITmfConfiguration> fTmfConfigurationTable = HashBasedTable.create();

/**
* @return a table mapping configuration id and trace (exp) to its configuration
*/
protected Table<String, ITmfTrace, ITmfConfiguration> getConfigurationTable(){
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If you put this public, then you can have the configurator as a separate class and the parent data provider factory doesn't need to extend that class (see IDataProviderFactory.getAdapter(), which needs to be overriden by the factory to return other objects that itself). I tried it with the InAndOutAnalysis and it worked.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have not understood what you propose here. Could you please try to rephrase or provide some code snippets? Thanks!

Copy link
Copy Markdown
Contributor

@bhufmann bhufmann Apr 7, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So, the IDataProviderFactory provides a getAdapter(class) method (

) that the trace server is using to get the configurator instance. The factory implementation can decide to implement itself the ITmfDataProviderConfigurator interface or overrides the getAdapter(...) method as below and return a different class that implements the ITmfDataProviderConfigurator interface. So, the getConfigurationTable() method needs to be public instead of protected to allow that the factory class to call that method.

public class MyDataProviderFactory implements IDataProviderFactory {
// ....
   private MyConfigurator fConfigurator = new MyConfigurator();

   @Override
    public <T> @Nullable T getAdapter(@Nullable Class<T> adapter) {
        if (adapter != null && adapter.equals(ITmfDataProviderConfigurator.class)) {
            return (T) fConfigurator;
        }
        return null;
    }

// ...
}

where:

public class MyConfigurator implements ITmfDataProviderConfigurator {
// ...
}

So, if MyConfigurator would extend your new AbstractTmfDataProviderConfigurator the factory class might call fConfigurator.getConfigurationTable() to get configurations per trace. Here is the change that I had to do for the getDescriptor(trace) method of InAndOutDataProviderFactory of your example :

  @Override
    public Collection<IDataProviderDescriptor> getDescriptors(ITmfTrace trace) {
        List<IDataProviderDescriptor> list = new ArrayList<>();
        list.add(DESCRIPTOR);
        for (ITmfConfiguration config : configurator.getConfigurationTable().column(trace).values()) {
            list.add(fConfigurator.getDescriptorFromConfig(config));
        }
        return list;
    }

Having said all the above, you might be just able to change the method configurator.getDescriptorFromConfig(...) and pass the trace as parameter. Then you don't need to make the getConfigurationTable() public.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I made the method public as you requested

return fTmfConfigurationTable;
}

/**
* Create instances implementing {@link ITmfConfiguration}. Override this
* method if the data provider configurator needs another configuration
* instead of the default {@link TmfConfiguration}
*
* @param configuration
* a object implementing implementing {@link ITmfConfiguration}.
* @return the instance of the class implementing {@link ITmfConfiguration}.
* Default is {@link TmfConfiguration}.
*/
protected ITmfConfiguration createConfiguration(ITmfConfiguration configuration) {
String description = configuration.getDescription();
if (configuration.getDescription().equals(TmfConfiguration.UNKNOWN)) {
description = "Data provider defined by configuration " + configuration.getName(); //$NON-NLS-1$
}

TmfConfiguration.Builder builder = new TmfConfiguration.Builder();
Comment thread
frallax marked this conversation as resolved.
builder.setId(configuration.getId())
.setSourceTypeId(configuration.getSourceTypeId())
.setName(configuration.getName())
.setDescription(description)
.setParameters(configuration.getParameters())
.build();
return builder.build();
}

@Override
public @NonNull IDataProviderDescriptor createDataProviderDescriptors(ITmfTrace trace, ITmfConfiguration configuration) throws TmfConfigurationException {

if (configuration.getName().equals(TmfConfiguration.UNKNOWN)) {
throw new TmfConfigurationException("Missing configuration name"); //$NON-NLS-1$
}

if (configuration.getSourceTypeId().equals(TmfConfiguration.UNKNOWN)) {
throw new TmfConfigurationException("Missing configuration type"); //$NON-NLS-1$
}

ITmfConfiguration config = createConfiguration(configuration);

applyConfiguration(trace, config, true);
if (fTmfConfigurationTable.contains(config.getId(), trace)) {
throw new TmfConfigurationException("Configuration already existis with label: " + config.getName()); //$NON-NLS-1$
}
fTmfConfigurationTable.put(config.getId(), trace, config);
return getDescriptorFromConfig(config);
}

/**
* @param config
* a configuration
* @return A data provider descriptor based on the configuration parameter
*/
protected abstract IDataProviderDescriptor getDescriptorFromConfig(ITmfConfiguration config);

/**
* This is the method that handles what happens when a configuration is
* applied
*
* @param trace
* trace to which the configuration should be applied
* @param config
* the configuration to be applied
* @param writeConfig
* true if the configuration should be written to disk, false
* otherwise
*/
protected abstract void applyConfiguration(ITmfTrace trace, ITmfConfiguration config, boolean writeConfig);

@Override
public void removeDataProviderDescriptor(ITmfTrace trace, IDataProviderDescriptor descriptor) throws TmfConfigurationException {
ITmfConfiguration creationConfiguration = descriptor.getConfiguration();
if (creationConfiguration == null) {
throw new TmfConfigurationException("Data provider was not created by a configuration"); //$NON-NLS-1$
}

String configId = creationConfiguration.getId();
ITmfConfiguration config = fTmfConfigurationTable.get(configId, trace);
if (config == null) {
return;
}
config = fTmfConfigurationTable.remove(configId, trace);
removeConfiguration(trace, config);
}

/**
* This is the method that handles what happens when a configuration is
* removed (e.g. remove analysis, dp etc)
*
* @param trace
* trace to which the configuration should be applied
* @param config
* the configuration to be applied
*/
protected abstract void removeConfiguration(@NonNull ITmfTrace trace, @NonNull ITmfConfiguration config);

/**
* Signal handler for opened trace signal. Will populate trace
* configurations
*
* @param signal
* the signal to handle
*/
@TmfSignalHandler
public void traceOpened(TmfTraceOpenedSignal signal) {
ITmfTrace trace = signal.getTrace();
if (trace == null) {
return;
}
try {
if (trace instanceof TmfExperiment) {
for (ITmfTrace tr : TmfTraceManager.getTraceSet(trace)) {
// Read configurations from sub-trace
List<ITmfConfiguration> configs = readConfigurations(tr);
readAndApplyConfiguration(trace, configs);
}
}
// Read configurations from trace or top level experiment
List<ITmfConfiguration> configs = readConfigurations(trace);
readAndApplyConfiguration(trace, configs);
} catch (TmfConfigurationException e) {
Activator.logError("Error applying configurations for trace " + trace.getName(), e); //$NON-NLS-1$
}
}

/**
* Handles trace closed signal
*
* @param signal
* the close signal to handle
*/
@TmfSignalHandler
public void traceClosed(TmfTraceClosedSignal signal) {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This this class handles tmf signals, we need to register to the signal manager and also derigster when it's disposed. You can register in the constructor.

TmfSignalManager.register(this);

Probably this class should have a dispose method to deregister from the signal manager. The factory dispose() method would call then this method.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

ITmfTrace trace = signal.getTrace();
fTmfConfigurationTable.column(trace).clear();
}

private void readAndApplyConfiguration(ITmfTrace trace, List<ITmfConfiguration> configs) throws TmfConfigurationException {
for (ITmfConfiguration config : configs) {
if (!fTmfConfigurationTable.contains(config.getId(), trace)) {
fTmfConfigurationTable.put(config.getId(), trace, config);
applyConfiguration(trace, config, false);
}
}
}

/**
* Reads the configurations for a given trace
*
* @param trace
* the trace to read configurations from
* @return list of configurations if any
* @throws TmfConfigurationException
* if an error occurs
*/
private @NonNull List<ITmfConfiguration> readConfigurations(@NonNull ITmfTrace trace) throws TmfConfigurationException {
IPath rootPath = getConfigurationRootFolder(trace);
File folder = rootPath.toFile();
List<ITmfConfiguration> list = new ArrayList<>();
if (folder.exists()) {
File[] listOfFiles = folder.listFiles();
for (File file : listOfFiles) {
IPath path = new Path(file.getName());
if (path.getFileExtension().equals(JSON_EXTENSION)) {
ITmfConfiguration config = TmfConfiguration.fromJsonFile(file);
list.add(config);
}
}
}
return list;
}

/**
* Serialize {@link ITmfConfiguration} to JSON file with name configId.json
*
* @param configuration
* the configuration to serialize
* @param rootPath
* the root path to store the configuration
* @throws TmfConfigurationException
* if an error occurs
* @since 9.5
*/
protected static void writeConfiguration(ITmfConfiguration configuration, IPath rootPath) throws TmfConfigurationException {
IPath supplPath = rootPath;
File folder = supplPath.toFile();
if (!folder.exists()) {
folder.mkdir();
}
supplPath = supplPath.addTrailingSeparator().append(configuration.getId()).addFileExtension(JSON_EXTENSION);
File file = supplPath.toFile();
try (Writer writer = new FileWriter(file)) {
writer.append(new Gson().toJson(configuration));
} catch (IOException | JsonParseException e) {
Activator.logError(e.getMessage(), e);
throw new TmfConfigurationException("Error writing configuration.", e); //$NON-NLS-1$
}
}

/**
* @param trace
* the trace to which the configuration should be applied to
* @return the path where the configuration should be stored
*/
@SuppressWarnings("null")
protected @NonNull abstract IPath getConfigurationRootFolder(@NonNull ITmfTrace trace);

/**
* Disposes the signal manager
*/
protected void dispose() {
TmfSignalManager.dispose();
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,6 @@
import org.eclipse.jdt.annotation.Nullable;
import org.eclipse.tracecompass.internal.tmf.core.Activator;
import org.eclipse.tracecompass.tmf.core.exceptions.TmfConfigurationException;

import com.google.gson.Gson;
import com.google.gson.JsonParseException;
import com.google.gson.annotations.Expose;
Expand All @@ -49,8 +48,11 @@ public class TmfConfiguration implements ITmfConfiguration {

/**
* The json file extension
*
* @since 9.5
* @deprecated use {@link AbstractTmfDataProviderConfigurator#JSON_EXTENSION} instead
*/
@Deprecated
public static final String JSON_EXTENSION = "json"; //$NON-NLS-1$
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We can't remove it just yet. It's api and needs to be deprecated first. In incubator master branch there is already a new class that uses it.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done


@Expose
Expand Down Expand Up @@ -327,7 +329,11 @@ public static ITmfConfiguration fromJsonFile(File jsonFile) throws TmfConfigurat
* @throws TmfConfigurationException
* if an error occurs
* @since 9.5
* @deprecated use
* {@link AbstractTmfDataProviderConfigurator#writeConfiguration(ITmfConfiguration, IPath)}
* instead
*/
@Deprecated
public static void writeConfiguration(ITmfConfiguration configuration, IPath rootPath) throws TmfConfigurationException {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We can't remove it just yet. It's api and needs to be deprecated first. In incubator master branch there is already a new class that uses it.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

IPath supplPath = rootPath;
File folder = supplPath.toFile();
Expand All @@ -343,4 +349,5 @@ public static void writeConfiguration(ITmfConfiguration configuration, IPath roo
throw new TmfConfigurationException("Error writing configuration.", e); //$NON-NLS-1$
}
}

}
Loading