Chapter 3. Launching a kermeta program in interpreted mode

You can start Kermeta interpreter on you kermeta code from a java program, however you must be aware of the run context. You'll probably run the interpreter in Eclipse environment or as a standard java application. Actually, the simpliest is to run in eclipse because you don't have to bother about the jar used by the interpreter and eclipse itself but you'll have to do some GUI integration (create a popup menu for example). On the other hand, you won't have to create a GUI, but you have to care about file name resolution and jar location.

[Tip]Tip

Do not be confused, you can run a standard java application from Eclipse IDE. In this case, you need to follow the explanation of Section 3.2, “Interpreted mode in standard java”.

3.1. Interpreted mode in Eclipse environment

Here is a typical code for starting a kermeta interpreter on a kermeta program.

[Note]Note

This sample is extracted from the kmLogo example which is distributed with Kermeta. You can retrieve this sample by doing the following steps.

File > new  > Example...  >  Kermeta samples  > km Logo tutorial (plugins for the main workbench) 

The action file corresponding to this section are in

fr.irisa.triskellkmlogo.ui/src/fr.irisa.triskell.kmlogo.ui.popup.actions
package fr.irisa.triskell.kmlogo.ui.popup.actions;

import java.util.Iterator;

import org.eclipse.core.resources.IFile;
import org.eclipse.jface.action.IAction;
import org.eclipse.jface.viewers.ISelection;
import org.eclipse.jface.viewers.StructuredSelection;
import org.eclipse.ui.IActionDelegate;
import org.eclipse.ui.IObjectActionDelegate;
import org.eclipse.ui.IWorkbenchPart;

import fr.irisa.triskell.eclipse.console.EclipseConsole;
import fr.irisa.triskell.eclipse.console.IOConsole;
import fr.irisa.triskell.eclipse.console.messages.ErrorMessage; 
import fr.irisa.triskell.eclipse.console.messages.InfoMessage;
import fr.irisa.triskell.eclipse.console.messages.OKMessage;
import fr.irisa.triskell.eclipse.console.messages.ThrowableMessage;
import fr.irisa.triskell.kmlogo.ui.RunLogoK;

/**
 * The action may take a long time, so the concrete action is embedded in a Runnable Thread
 *
 */
public class RunLogo implements IObjectActionDelegate, Runnable {

   protected StructuredSelection currentSelection;
   protected IFile logoFile;
    
   public static final String LOGO_SIMULATOR_KERMETA_CODE = 
      "platform:/plugin/fr.irisa.triskell.kmlogo.model/logo/5.Simulator/LogoSimulator.kmt"; 1
	
   /**
    * Constructor for Action1.
    */
   public RunLogo() {
      super();
   }
	
   public void run() {
		
      IOConsole console = new EclipseConsole("Logo Simulator"); 2
      console.println(new InfoMessage("Launching logo interpreter on file : " + logoFile.getLocation().toOSString() + "..."));
		
      try {			
				
         String file_uri = "file:/" + logoFile.getLocation().toOSString();
		    
         Interpreter interpreter = new Interpreter(LOGO_SIMULATOR_KERMETA_CODE, InterpreterMode.RUN, null);
         interpreter.setStreams(console);		3
         interpreter.setEntryPoint("kmLogo::Interpreter", "execute");4
         String[] parameters = new String[1]; 5
         parameters[0] = file_uri;
         interpreter.setParameters(parameters);
			
         // Add some URL to the classloader of the interpreter : needed if your code use some extra java classes (via extern for example)
         // use a set for building the URL (in case some may fail due to malformed URL
         // Note : URL must end with a / if this is a directory, if not, this is considered as a jar by the classloader
         Set<URL> urlsSet = new LinkedHashSet<URL>();
         // URL used when run in a runtimeworkbench, this allows to debug the plugin
         safeAddURLAsString(urlsSet, "file://" + FileLocator.resolve(Platform.getBundle("fr.irisa.triskell.kmlogo.model").getEntry("/bin/")));
         // add this plugin as a deployed plugin
         Bundle bundle = org.eclipse.core.runtime.Platform.getBundle("fr.irisa.triskell.kmlogo.model");
         if(bundle != null){
            urlsSet.add(FileLocator.resolve(bundle.getEntry("/")));
         }
         // convert the set into an array
         URL[] urls = new URL[urlsSet.size()];
         int i = 0;
         for (URL url : urlsSet) {
            urls[i] = url;
            i++;
         }
         URLClassLoader cl = new URLClassLoader(urls, interpreter.getClass().getClassLoader());
         Thread.currentThread().setContextClassLoader(cl);
			
         interpreter.launch(); 6
			
         console.println(new OKMessage("Execution terminated successfully."));
			
			
      } catch (Throwable e) { 7
         console.println(new ErrorMessage("Logo runtime error : "));
         console.println(new ThrowableMessage(e));	
         e.printStackTrace();
      }
   }

   /**
    * @see IObjectActionDelegate#setActivePart(IAction, IWorkbenchPart)
    */
   public void setActivePart(IAction action, IWorkbenchPart targetPart) {
   }

   /**
    * @see IActionDelegate#run(IAction)
    * Create a new thread for this concrete action
    */
   public void run(IAction action) {
      new Thread(this).start();
   }

   /**
    * @see IActionDelegate#selectionChanged(IAction, ISelection)
    * This allow to retrieve the file selected by the user when activating the popup
    */
   public void selectionChanged(IAction action, ISelection selection) {
		
      if (selection instanceof StructuredSelection)
      {
         // the selection should be a single file
         currentSelection = (StructuredSelection)selection;
         Iterator it = currentSelection.iterator();

         while(it.hasNext()) {
            logoFile = (IFile)it.next();
         }
      }
   }
   /**
    * add a new URL to the set
    * Doesn't fail if the URL is malformed, in that case, only a warning is raised, 
    * @param urlsSet : set that will contain the URL built
    * @param url : String of the URL to build
    */
   private static void safeAddURLAsString(Set<URL> urlsSet, String url){
      try{
         urlsSet.add(new URL(url));
      } catch (MalformedURLException e) {
         Activator.logWarningMessage(
            "problem adding an entry of the classpath, "
            + url + " cannot be added in classloader", e);
      }
   }

}
		

1

Path to the kermeta program that will be run.

2

Kermeta stdio.writeln outputs must be redirected to an IOConsole. This EclipseConsole will print to the user console in eclipse.

The plugin fr.irisa.triskell.eclipse.util also provides so other console kind like LocalIOConsole that prints to the standard streams.

3

Tells the interpreter where the stdio will be written.

4

Indicates the main class and the operation that will be started.

5

Pass the String parameters to the interpreter. This must conforms to the expected parameters of the main operation specified as entry point.

6

Finally starts the interpreter and wait for the end of the kermeta program.

[Note]Note

In the Interpreter API, it exist some other way to start the program, for example launchAndWait() which doesn't release the interpreter memory at the end of the operation which is useful in some special applications. Feel free to ask question to the development team.

7

Use various means to tell the user that something wrong occurred. However, if you expect some exception and haven't catched them in the kermeta program (for example when checking a model invariant, you may add here some code to report a better message to your users).

3.2. Interpreted mode in standard java

Most of the elements seen in the previous section is still correct (especially how to initialize the Interpreter with your kermeta program and passing the parameters). In addition to that you must be aware of some point.

[Tip]Tip

A good sample for building your own standard java calling kermeta interpreter is to look at the RunCommandLine.java class which implements a command line argument parser and calls the Interpreter on top of a standalone distribution. http://gforge.inria.fr/scm/viewvc.php/trunk/kermeta_projects/fr.irisa.triskell.kermeta.interpreter/src/java/org/kermeta/interpreter/api/RunCommandLine.java?view=markup&root=kermeta

3.2.1. platform:/resource and platform:/plugin resolution

These URI are specific to Eclipse and aren't known by default in Java. You'll need to guide indicate how to map them to the classical file:// URIs

This is done by adding some URI map entry in EMF.

import org.eclipse.emf.ecore.resource.impl.ExtensibleURIConverterImpl;
			
ExtensibleURIConverterImpl.URI_MAP.put(URI.createURI("platform:/plugin/"), URI.createURI("file://path_to_you_folder_containing_a_kind_of_plugin_structure"));
ExtensibleURIConverterImpl.URI_MAP.put(URI.createURI("platform:/resource/"), URI.createURI("file://path_to_you_folder_containing_a_kind_of_workspace_structure"));

Example 3.1. adding URI map entries in EMF (java)


[Tip]Tip

In the above sample, we map only two folders, however you can reach more complex mapping sceme. Please have a look in eclispe help for more details. http://help.eclipse.org/galileo/topic/org.eclipse.emf.doc/references/javadoc/org/eclipse/emf/ecore/resource/impl/ExtensibleURIConverterImpl.html#getURIMap()

3.2.2. classpath

Obviously, as you run as a standard java application you'll need to include inthe classpath all the jar that constitute Kermeta interpreter and their dependencies to eclipse jars.

If your code use extern call, you'll also need to provide the jar that contains the java implementation of those extern operation.

[Tip]Tip

The standalone distribution of kermeta is a repackaging of kermeta and all its dependencies in a convenient jar (or set of jars) that doesn't include those nasty version number eclipse use. It's more convenient to build your classpath from it.