Introduction
Today we are going to create a runnable JAR file to run our code generation process. Previously we saw how to use Java in order to ease the code generation and be able to introduce more complex logic in the templates. So far we have used Eclipse to start our code generation. We were locating the workflow file and executing it. Of course, using Eclipse is not always the option. A better and more user-friendly approach would be to have some executable, which will then start the generation process. We can use that executable to run it ourselves or make it part of some build scripts.
Well, that is possible and we will see how to do it. Basically, we will create one runnable JAR file, which will run the workflow file. In this tutorial you will see how.
Adjusting the workflow file to be run by a runnable JAR
One of the files which I will update is the workflow file. The change is not significant. I want to be able to pass the paths to the XML files from the command prompt. Also, I want to pass the output directory. For this purpose I will remove the defined properties for those paths. Since I will be taking them from outside I will not need them in the file. I will keep their usage though. In the top of the workflow file, which we had developed so far I will remove the three path properties and I will leave only the one for the beautifier configuration file (you can also pass it as an argument if you want). Following is the updated workflow file. Notice that only the three properties are removed, everything else stays the same.
<!-- Definition of the code generation workflow --> <workflow> <property name="beautifierConfigFile" value="file:///C:\MyPrograms\eclipse-cpp-2019-12-R-win32-x86_64\eclipse\eclipse-workspace\EMFTraining\Inputs\BeautificationRules.xml" /> <!-- Standart setup routine--> <bean class="org.eclipse.emf.mwe.utils.StandaloneSetup" > <platformUri value=".."/> </bean> <!-- Set up EMF for standalone execution and maps to the Java classes, generated from the Ecore model --> <bean class="org.eclipse.xtend.typesystem.emf.Setup" > <EPackageClass value="Translations.TranslationsPackage"/> <ExtensionMap> <from value="xml"/> <to value="Translations.util.TranslationsResourceFactoryImpl"/> </ExtensionMap> </bean> <!-- Instantiate metamodel --> <bean id="mm_emf" class="org.eclipse.xtend.typesystem.emf.EmfRegistryMetaModel"/> <!-- Parses the EN translations xml file and store all objects in slot 'model' --> <component class="org.eclipse.emf.mwe.utils.Reader"> <uri value="${modelFileEn}" /> <modelSlot value="model" /> </component> <!-- generate code for EN translations--> <component class="org.eclipse.xpand2.Generator"> <metaModel idRef="mm_emf" /> <!-- Call the Main function of the Main.xpt template--> <expand value="templates::MainTemplate::Main('en') FOR model" /> <!-- Files generated with this outlet will be beautified using the rules in the config file--> <outlet path="${outputPath}"> <postprocessor class="org.eclipse.xpand.support.cdt.CppBeautifier" configFile="${beautifierConfigFile}"/> </outlet> <!-- Path to the folder where the files are generated. Used when lookig for protected regions--> <prSrcPaths value="${outputPath}"/> </component> <!-- Parses the BG translations xml file and store all objects in slot 'model' --> <component class="org.eclipse.emf.mwe.utils.Reader"> <uri value="${modelFileBg}" /> <modelSlot value="model" /> </component> <!-- generate code for BG translations --> <component class="org.eclipse.xpand2.Generator"> <metaModel idRef="mm_emf" /> <!-- Call the Main function of the Main.xpt template--> <expand value="templates::MainTemplate::Main('bg') FOR model" /> <!-- Files generated with this outlet will be beautified using the rules in the config file--> <outlet path="${outputPath}"> <postprocessor class="org.eclipse.xpand.support.cdt.CppBeautifier" configFile="${beautifierConfigFile}"/> </outlet> <!-- Path to the folder where the files are generated. Used when lookig for protected regions--> <prSrcPaths value="${outputPath}"/> </component> </workflow>
The entry point of the runnable JAR
In order to run the JAR file we need to have a Main function. For this purpose I am going to create a new class in the javaHelpers folder and name it Main.java. Below is the code I have written:
package javaHelpers; import java.util.HashMap; import org.eclipse.emf.mwe.core.WorkflowEngine; public class Main { /** * The main method which is called when the program is started. * @param args: Command-line arguments. */ public static void main(String[] args) { if(args.length >= 3) { HashMap<String, String> properties = new HashMap<String, String>(); properties.put("modelFileEn", args[0]); properties.put("modelFileBg", args[1]); properties.put("outputPath", args[2]); runWorkflow(properties); } else { System.out.println("Application usage: java -jar <NameOfExecutable>.jar <EnglishLanguageInputXmlFilePath> <BulgarianLanguageInputXmlFilePath>"); } } /** * Runs the workflow file which processes the code generation. * @param properties: The workflow properties. */ public static void runWorkflow(HashMap<String, String> properties) { WorkflowEngine runner = new WorkflowEngine(); String workFlowFile = "./workflows/Generate.mwe"; try { runner.run(workFlowFile, null, properties, null); } catch (Exception e) { e.printStackTrace(); } } }
It has two methods: main and runWorkflow(). Firstly, we are importing the required packages. Then we have the methods’ definitions.
The main() method
The first thing we do in the main method is to check whether the user has provided three arguments – path to the main language XML, path to the other language XML and the output path. If you have more languages you will have to provide more arguments. You can be more clever and provide a better solution at getting the paths to the XML files. I am just doing a quick solution, only for the purpose of the tutorial.
If three arguments or more are provided I create one HashMap and fill it with these arguments. Notice how I am providing the names of the properties, that were used in the workflow file as keys. This way Xpand will automatically pick the correct values for the given properties. I am assuming that the first argument will be the path to the main language, the second one will be the path to the other language and the third argument – the path to the output folder. After I have filled the HashMap I am calling the runWorkflow() method.
In case the user has provided less than three arguments I am printing one message showing how the application shall be used. As I said earlier I don’t have any explicit checks whether the user has provided correct values since this is just a tutorial and I try to keep it simple. But in your production application you would like to be more careful.
The runWorkflow() method

Right-click on Java Application >> New Configuration

Fill the Name field and in the Main class field click on the Search button and find the class we created earlier. It should look like this:

Once this is done click on Apply and close the popup.
Now it is time to export the runnable JAR. Right click on the project and select Export… In the popup that appears find Runnable JAR file

In the next popup in the Launch configuration select the configuration we created previously. Then using the Browse button select where to export the JAR file. In the Library handling: select the second option. This option is with less requirements for licensing of the software. When done click on the Finish button.

You should now have the JAR file exported and you can go ahead and test it. Let’s see what we can do next.
Testing the runnable JAR file
Since I have used hard-coded path for the workflow file I will have to put my runnable JAR file inside the src folder of the project in order to test it. If you have done like me, make sure to put it there as well. Now open a command prompt or a PowerShell (I prefer the second one). Type the following command:
java -jar .\Main.jar .\inputs\translations_en.xml .\inputs\translations_bg.xml ..\src-gen\
Provide the paths for the XML files accordingly. Keep in mind that the first path is the main language XML path, the second is the other language and the third – the output path. The output shall be similar to this:

If everything goes well, you should check your output folder and you should see the generated files. That’s it. Now you can generate code without the need to open Eclipse.
Conclusion
In this tutorial we saw how to create a runnable JAR file. We can use that file in order to run the generation without the need of Eclipse. It is very convenient. It can be used by the users directly in the command prompt or it can be made a part of the build process. With this we finish the project for the translation tables. I will once again upload here the project so far so you can compare against your project. Also it is available in GitHub.
In the next blog post we will see how to do a constraint checking on the input XML files. Our series is almost over. We have done some interesting things and I hope you have enjoyed it too.

Passionate developer, loving husband and caring father. As an introvert programming is my life. I work as a senior software engineer in a big international company. My hobbies include playing computer games (mostly World of Warcraft), watching TV series and hiking.