Skip to content

Create a new Framesoc tool

Generoso Pagano edited this page May 28, 2015 · 41 revisions

Summary

This page describes how to create a new Framesoc tool. Within this tutorial we also provide some usage example of the Eclipse Selection Service and the Framesoc query API. The tutorial has been developed using Eclipse Luna and Framesoc v1.0.6. The corresponding source code is available in the fr.inria.soctrace.framesoc.tutorials.tool plugin of this repository.

Requirements

Introduction

A Framesoc tool is an Eclipse plugin extending the fr.inria.soctrace.framesoc.core.tool extension point defined in the fr.inria.soctrace.framesoc.core plugin. A Framesoc tool can be launched by Framesoc, perform an analysis and produce analysis results. These results can be stored in the Framesoc database. More details about tool and analysis result representation in the Framesoc datamodel are available in the Technical Report RT-427 .

Tutorial

Create the Framesoc tool plugin

  • Create a new plugin: File -> New -> Plug-in Project.

    • Specify as project name fr.inria.soctrace.framesoc.tutorials.tool and press Next.
    • Do not change anything and press Next again.
    • Uncheck the Create a plug-in using one of the templates option, then press Finish.
  • Open the file META-INF/MANIFEST.MF, if not already opened automatically, to add the required dependencies.

    • This file is normally opened using the Plug-in Manifest Editor.

    • In this editor, select the Dependencies tab and press Add.

    • A dialog is shown: in the text field write fr.inria.soctrace.lib and add all the plugins of the Framesoc library.

    • Press Add again, and this time write fr.inria.soctrace.framesoc in order to add the plugins fr.inria.soctrace.framesoc.core and fr.inria.soctrace.framesoc.ui.

    • At this point your list of required plugins should contain:

      org.eclipse.ui
      org.eclipse.core.runtime
      fr.inria.soctrace.lib.model
      fr.inria.soctrace.lib.query
      fr.inria.soctrace.lib.search
      fr.inria.soctrace.lib.slf4j
      fr.inria.soctrace.lib.storage
      fr.inria.soctrace.lib.utils
      fr.inria.soctrace.framesoc.core
      fr.inria.soctrace.framesoc.ui
      
    • Save the MANIFEST.MF. Note: it is a good practice to save this file immediately after each modification, in order to take advantage at the highest level of the automatic code generation (see below). In the following it will be assumed that this file is saved after each modification.

  • Create the tool extension.

    • Open the Extensions tab and press Add.
    • Write fr.inria.soctrace.framesoc.core.tool in the filter, and select the corresponding extension point.
    • Press Finish
    • Right-click on the extension you just created (in the list box), then New -> tool.
  • The documentation of the extension point fields can be accessed selecting the fr.inria.soctrace.framesoc.core.tool line and clicking on Show extension point description on the right.

    id - The ID of the tool. This is NOT the database ID. It is an identificator having the same format of java     
    packages (i.e., x.y.z.k). Note that each tool must have a globally unique id in the system.
    class - The main class of the tool, extending the FramesocTool abstract class. See its documentation for more details.
    type - The type of the tool.
    name - The name of the tool. Note that each tool must have a globally unique name in the system.
    doc - Launching documentation. Optional.
    
  • Fill the the extension element details as follow:

    • id: fr.inria.soctrace.framesoc.tutorials.tool.example
    • class: fr.inria.soctrace.framesoc.tutorials.tool.ExampleTool
    • type: ANALYSIS
    • name: Framesoc Tutorial Tool
    • doc: Launch the tool and enjoy.
  • Create the class needed by the extension.

    • Click on the class link.

    • Do not change anything and press Finish

    • The following class should be automatically created:

      package fr.inria.soctrace.framesoc.tutorials.tool;
      
      import fr.inria.soctrace.framesoc.core.tools.model.FramesocTool;
      import fr.inria.soctrace.framesoc.core.tools.model.IFramesocToolInput;
      
      public class ExampleTool extends FramesocTool {
      
          public ExampleTool() {
      	    // TODO Auto-generated constructor stub
          }
      
          @Override
          public void launch(IFramesocToolInput input) {
      	    // TODO Auto-generated method stub
          }
      }
    • Add the following line to the launch() method body:

      System.out.println("Hello World!");
    • Try to launch the Eclipse Application and test the tool.

      • Right-click on the tool plugin (in the Package explorer on the left), Run as -> Eclipse Application.
      • Framesoc -> Trace Analysis -> Launch Analysis Tool.
      • Select the Framesoc Tutorial Tool and press OK.
      • In the console of the development Eclipse, you should see Hello World!.

Get the input

Up to this point, our tool is simply launched by Framesoc with no input. A plugin tool can specify the form of is input by extending the extension point fr.inria.soctrace.framesoc.ui.input.toolInput. This extension point enables to specify a factory class that will be used to create a graphical composite that extends a given base class. This composite will contain the graphical elements that the user will use to specify the input. In this tutorial we will simply use two radio buttons, to know if the user wants to know information about event types or event producers.

We will create the following classes:

  • ExampleToolInputCompositeFactory: factory class extending the AbstractToolInputCompositeFactory, whose API provides the method getComposite(Composite parent, int style), called by Framesoc to get the actual composite element. We need to use a factory since the Eclipse runtime cannot instantiate a class provided by an extension if this class does not have a default constructor (a constructor without parameters). The Composite class has only a constructor with parameters.
  • ExampleToolInputComposite: it is the composite returned by the getComposite() method cited above. It extends the AbstractToolInputComposite, whose API provides a IFramesocToolInput getToolInput() method, called by Framesoc to retrieve the the input that will be passed to the tool when launched.
  • ExampleToolInput: it is the input class returned by the getToolInput() method cited above. It implements the IFramesocToolInput interface. This class has an inner enumeration, QueryEntity, used to model the entity used for the analysis (event types or event producers).

The detailed procedure to create the above classes, with the corresponding code, follows.

  • Create an extension to the fr.inria.soctrace.framesoc.ui.input.toolInput extension point, following the same procedure as before for the tool extension point.

  • Fill the extension element details as follow:

    • toolId: fr.inria.soctrace.framesoc.tutorials.tool.example
    • compositeFactory: fr.inria.soctrace.framesoc.tutorials.tool.ExampleToolInputCompositeFactory
  • As done before, click on the link corresponding to the class (compositeFactory this time) and press Finish. The factory class is automatically created and has the following form:

    package fr.inria.soctrace.framesoc.tutorials.tool;
    
    import org.eclipse.swt.widgets.Composite;
    
    import fr.inria.soctrace.framesoc.ui.input.AbstractToolInputComposite;
    import fr.inria.soctrace.framesoc.ui.input.AbstractToolInputCompositeFactory;
    
    public class ExampleToolInputCompositeFactory extends
          AbstractToolInputCompositeFactory {
    
      public ExampleToolInputCompositeFactory() {
          // TODO Auto-generated constructor stub
      }
    
      @Override
      public AbstractToolInputComposite getComposite(Composite parent, int style) {
          // TODO Auto-generated method stub
          return null;
      }
    }

    The API of this factory requires to provide a graphical composite for the input. This composite extends the AbstractToolInputComposite base class.

  • Modify the factory getComposite() method as follows:

      @Override
      public AbstractToolInputComposite getComposite(Composite parent, int style) {
           return new ExampleToolInputComposite(parent, style);    
      }
  • Create the composite class fr.inria.soctrace.framesoc.tutorials.tool.ExampleToolInputComposite, using the following code:

    package fr.inria.soctrace.framesoc.tutorials.tool;
    
    import org.eclipse.swt.SWT;
    import org.eclipse.swt.events.SelectionAdapter;
    import org.eclipse.swt.events.SelectionEvent;
    import org.eclipse.swt.layout.GridLayout;
    import org.eclipse.swt.widgets.Button;
    import org.eclipse.swt.widgets.Composite;
    
    import fr.inria.soctrace.framesoc.core.tools.model.IFramesocToolInput;
    import fr.inria.soctrace.framesoc.tutorials.tool.ExampleToolInput.QueryEntity;
    import fr.inria.soctrace.framesoc.ui.input.AbstractToolInputComposite;
    
    public class ExampleToolInputComposite extends AbstractToolInputComposite {
    
        // Tool input that will be passed to the tool by Framesoc when the tool is launched
        private ExampleToolInput input = new ExampleToolInput();
    
        public ExampleToolInputComposite(Composite parent, int style) {
      	  super(parent, style);
      	  setLayout(new GridLayout(1, false));
    
      	  final Button types = new Button(this, SWT.RADIO);
      	  types.setText("Event Types");
      	  types.addSelectionListener(new SelectionAdapter() {
      		  @Override
      		  public void widgetSelected(SelectionEvent e) {
      			  if (types.getSelection()) {
      				  update(QueryEntity.TYPES);
      			  }
      		  }
      	  });
    
      	  // default value: types
      	  types.setSelection(true);
      	  input.setQueryEntity(QueryEntity.TYPES);
    
      	  final Button producers = new Button(this, SWT.RADIO);
      	  producers.setText("Event Producers");
      	  producers.addSelectionListener(new SelectionAdapter() {
      		  @Override
      		  public void widgetSelected(SelectionEvent e) {
      			  if (producers.getSelection()) {
      				  update(QueryEntity.PRODUCERS);
      			  }
      		  }
      	  });
        }
    
        private void update(QueryEntity entity) {
      	  // react to input change
      	  input.setQueryEntity(entity);
      	  // ask the Framesoc dialog to update the OK button
      	  dialog.updateOk();
        }
    
        @Override
        public IFramesocToolInput getToolInput() {
      	  return input;
        }
    
    }
  • Create the input class fr.inria.soctrace.framesoc.tutorials.tool.ExampleToolInput using the following code:

    package fr.inria.soctrace.framesoc.tutorials.tool;
    
    import fr.inria.soctrace.framesoc.core.tools.model.IFramesocToolInput;
    
    public class ExampleToolInput implements IFramesocToolInput {
    
        public static enum QueryEntity {
      	  PRODUCERS,
      	  TYPES;
        }
      
        private QueryEntity queryEntity;
      
        @Override
        public String getCommand() {
      	  return "";
        }
    
        public QueryEntity getQueryEntity() {
      	  return queryEntity;
        }
    
        public void setQueryEntity(QueryEntity queryEntity) {
      	  this.queryEntity = queryEntity;
        }
    
        @Override
        public String toString() {
      	  return "ExampleToolInput [queryEntity=" + queryEntity + "]";
        }
    
    }
  • Validate the input.

    • The composite reacts to changes in the GUI (widgetSelected() methods), updating the current input (see ExampleToolInput below) and notifying the Framesoc launch dialog that a change has been done. The Framesoc dialog, in the updateOk() method, asks the tool to validate the input, to enable/disable the OK button (launch button). The implementation of updateOK() calls the canLaunch() method defined by the IFramesocTool interface. The base implementation of this method (provided by the FramesocTool abstract class) always says that the input is OK. To customize this behavior we have to override the canLaunch() method in our tool implementation.

    • Add the canLaunch() method to the ExampleTool implementation:

      @Override
      public ParameterCheckStatus canLaunch(IFramesocToolInput input) {
          if (input instanceof ExampleToolInput) {
      	    ExampleToolInput exampleInput = (ExampleToolInput) input;
      	    if (exampleInput.getQueryEntity() != null ) {
      		    System.out.println("Input is OK: " + exampleInput);
      		    return new ParameterCheckStatus(true, "");		
      	    }
          }
          return new ParameterCheckStatus(false, "Wrong input");
      }
  • Just for the fun, modify the tool launch() method as follow, to use the input:

    @Override
    public void launch(IFramesocToolInput input) {
        System.out.println("Hello World!");
        ExampleToolInput exampleInput = (ExampleToolInput) input;
        System.out.println("My input is valid: " + exampleInput);
    }

Create a view

A Framesoc tool typically opens a view in its launch() method, to provide the analyst with a GUI to perform a specific analysis. For the sake of this tutorial, we will create an Eclipse view displaying some information related to the trace currently selected in the Framesoc trace explorer.

  • Create an extension for the org.eclipse.ui.views extension point, following the same procedure as before for the tool and toolInput extension points.

  • Fill the extension element details as follow:

    • id: fr.inria.soctrace.framesoc.tutorials.tool.ExampleView
    • name: Framesoc Tutorial Tool
    • class: fr.inria.soctrace.framesoc.tutorials.tool.ExampleView
  • As done before, click on the link corresponding to the class and press Finish.

  • Replace the content of the view class that has been automatically created with the following:

      package fr.inria.soctrace.framesoc.tutorials.tool;
    
      import fr.inria.soctrace.lib.model.Trace;
      import org.eclipse.jface.viewers.ArrayContentProvider;
      import org.eclipse.jface.viewers.LabelProvider;
      import org.eclipse.jface.viewers.ListViewer;
      import org.eclipse.swt.SWT;
      import org.eclipse.swt.layout.GridData;
      import org.eclipse.swt.layout.GridLayout;
      import org.eclipse.swt.widgets.Composite;
      import org.eclipse.swt.widgets.Label;
      import org.eclipse.swt.widgets.Text;
      import org.eclipse.ui.part.ViewPart;
    
      import fr.inria.soctrace.framesoc.tutorials.tool.ExampleToolInput.QueryEntity;
    
      public class ExampleView extends ViewPart {
    
          public final static String ID = "fr.inria.soctrace.framesoc.tutorials.tool.ExampleView"; //$NON-NLS-1$
    
          private Text txtTrace;
          private Text txtEntity;
          private QueryEntity queryEntity;
          private ListViewer listViewer;
    
          @Override
          public void createPartControl(Composite parent) {
    
      	    /** Layout */
      	    parent.setLayout(new GridLayout(2, false));
    
      	    /** Selected trace */
      	    Label lblSelectedTrace = new Label(parent, SWT.NONE);
      	    lblSelectedTrace.setLayoutData(new GridData(SWT.RIGHT, SWT.CENTER, false, false, 1, 1));
      	    lblSelectedTrace.setText("Selected Trace");
      	    txtTrace = new Text(parent, SWT.BORDER);
      	    txtTrace.setEditable(false);
      	    txtTrace.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false, 1, 1));
    
      	    /** Selected Entity */
      	    Label lblQueryEntity = new Label(parent, SWT.NONE);
      	    lblQueryEntity.setLayoutData(new GridData(SWT.RIGHT, SWT.CENTER, false, false, 1, 1));
      	    lblQueryEntity.setText("Query Entity");
      	    txtEntity = new Text(parent, SWT.BORDER);
      	    txtEntity.setEditable(false);
      	    txtEntity.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false, 1, 1));
      	    // init default values
      	    queryEntity = QueryEntity.TYPES;
      	    txtEntity.setText(queryEntity.toString());
    
      	    /** Entities */
      	    new Label(parent, SWT.NONE);
      	    listViewer = new ListViewer(parent, SWT.BORDER | SWT.V_SCROLL);
      	    listViewer.getList().setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true, 1, 1));
      	    // we use an array content provider, since the input is a list of string
      	    listViewer.setContentProvider(new ArrayContentProvider());
      	    // use a base label provider, which simply calls the toString() methods on the
      	    // elements of the input
      	    listViewer.setLabelProvider(new LabelProvider());
    
          }
    
          /**
           * Set the tool input in the view
           * 
           * @param exampleInput
           *            tool input
           */
          public void setInput(ExampleToolInput exampleInput) {
      	    queryEntity = exampleInput.getQueryEntity();
      	    txtEntity.setText(queryEntity.toString());
          }
    
          @Override
          public void setFocus() {
      	    // nothing to do
          }
    
     	    private void updateView(Trace trace) {
                      // TODO, see below
              }
      }
  • Modify the tool launch() method, in order to make it open the view:

      @Override
      public void launch(IFramesocToolInput input) {
          System.out.println("Hello World!");
          ExampleToolInput exampleInput = (ExampleToolInput) input;
          System.out.println("My input is valid: " + exampleInput);
      
          // open the view
          IWorkbenchWindow window = PlatformUI.getWorkbench().getActiveWorkbenchWindow();
          try {
      	    ExampleView view = (ExampleView) window.getActivePage().showView(ExampleView.ID);
      	    view.setInput(exampleInput);
          } catch (Exception e) {
      	    e.printStackTrace();
          }
      }
    

The view we implemented so far simply creates some graphical elements, without providing any behavior.

In the following sections we will register to the Eclipse Selection Service to get the trace currently selected in the Framesoc trace browser, and we will use the Framesoc query API to perform simple requests on the trace.

Using the Eclipse Selection Service

The Framesoc trace browser is a selection provider on the Eclipse Selection Service. This means that another eclipse module may register a listener to this service to get the selection of the trace browser. To do that in our view we have to modify our view class.

  • Define a listener class, as a field of the ExampleView class, using the following code:

      /**
       * The listener we register with the selection service
       */
      private ISelectionListener listener = new ISelectionListener() {
          public void selectionChanged(IWorkbenchPart sourcepart, ISelection selection) {
      	    // we ignore our own selections
      	    if (sourcepart != ExampleView.this) {
      		    if (!TraceSelection.isSelectionValid(selection))
      			    return;
      		    List<Trace> traces = TraceSelection.getTracesFromSelection(selection);
      		    if (traces.isEmpty())
      			    return;
      		    updateView(traces.get(0));
      	    }
          }
      };

    This listener checks if the selection passed is a valid trace selection, then it gets the first selected trace (more than one trace can be selected at once). At the end, the updateView() method is called (see below).

  • Register the listener, adding the following code at the end of the createPartControl() method:

     // register the selection listener
     getSite().getWorkbenchWindow().getSelectionService().addSelectionListener(listener);
  • Override the dispose() method of the view, to unregister the listener:

      @Override
      public void dispose() {
          getSite().getWorkbenchWindow().getSelectionService().removeSelectionListener(listener);
          super.dispose();
      }

Using the Framesoc query API

When a new trace is selected in the Framesoc trace browser, we want to update our view displaying the instances of the required entity (event types or event producers) for that trace. To do that, we use the Framesoc query API to perform a query to the database and get the list of event types or event producers for the selected trace. This functionality is implemented in the updateView() method.

  • Modify the implementation of the view updateView() method as follows:

      /**
       * Updates the view with the current selected traces and the list of entities corresponding to
       * the 'query entity'.
       * 
       * @param trace
       *            selected trace
       */
      private void updateView(Trace trace) {
        txtTrace.setText(trace.getAlias());
        List<String> input = new ArrayList<>();
        TraceDBObject traceDB = null;
        try {
          traceDB = TraceDBObject.openNewIstance(trace.getDbName());
          if (queryEntity.equals(QueryEntity.TYPES)) {
              EventTypeQuery etq = new EventTypeQuery(traceDB);
              List<EventType> etl = etq.getList();
              for (EventType et : etl) {
      	        input.add(et.getName());
              }
          } else if (queryEntity.equals(QueryEntity.PRODUCERS)) {
              EventProducerQuery epq = new EventProducerQuery(traceDB);
              List<EventProducer> epl = epq.getList();
              for (EventProducer ep : epl) {
      	        input.add(ep.getName());
              }
          }
        } catch (SoCTraceException e) {
          e.printStackTrace();
        } finally {
          DBObject.finalClose(traceDB);
        }
        listViewer.setInput(input);
      }

    This method updates the text field containing the trace alias, then performs a query for event types or event producer, according to the passed tool input (the query entity).

    Using the Framesoc query API requires the following steps:

    • Open a connection to the database. For example:

        traceDB = TraceDBObject.openNewIstance(trace.getDbName());
    • Create the query object corresponding to the required entity. For example:

        EventTypeQuery etq = new EventTypeQuery(traceDB);
    • Even if not done in our simple example, it is possible to add some conditions to the query object. This is explained in detail in this tutorial.

    • Get the result of the query, as a list of object of the required entity. For example:

       List<EventProducer> epl = epq.getList();
      
    • Close the connection to the database. A good practice is to do this in the finally block of a try/catch. For example:

       finally {
          DBObject.finalClose(traceDB);
       }
      

Conclusion

In this tutorial we created a simple Framesoc analysis tool (fr.inria.soctrace.framesoc.core.tool extension point), getting an input in the Framesoc launch dialog (fr.inria.soctrace.framesoc.ui.input.toolInput extension point) and displaying an Eclipse view (org.eclipse.ui.views extension point). This tool gets as input the entity (event types or event producers) to be used to perform a query on the trace currently selected in the Framesoc trace browser. The result of this query is displayed in the view opened by the tool.

It is worth noting that this is only a toy tool, used only to explain the basic concepts of Framesoc tools. In particular, the use of the Framesoc extension point for the input is a bit artificial, since the tool opens a view, and it could require its input directly in the view. On the contrary, for importer tools, it is common to use the Framesoc input extension point to get the input from the launch dialog, since importers don't typically have an associated view. This tutorial explains in detail how to create an importer tool.

Clone this wiki locally