Monday, March 10, 2014

Creating Eclipse Plugin Project programmatically

In this article, I will explain how to create an eclipse plugin project programmatically.

Do check out the deployable plugin if you want to see the whole code or if you want to install that in your eclipse. The source plugin is also available if you want to check out the code.

The steps to create a plugin project are:
  1.  Create a IRunnableWithProgress and write your code inside it's run() method to create the plugin since is it a long running process.
  2. Create a IJavaProject and attach IProjectDescription to it.
  3. Set required Natures to the project
    • "org.eclipse.jdt.core.javanature" and "org.eclipse.pde.PluginNature" are
      mandatory for any plugin project
  4. Set required Builders to the project
    • "org.eclipse.jdt.core.javabuilder", "org.eclipse.pde.ManifestBuilder",
      "org.eclipse.pde.SchemaBuilder" are mandatory for
      any plugin project
  5. Open the project
  6. Create required source folders (eg, src folder)
  7. Set raw Classpath to the plugin project.
  8. Set output location for the project
  9. Create Manifest file
  10. Create Build.props file
  11. Create an activator class for the project
  12. Create any required artifacts, for ex, Java files, props files, etc
Continue reading the rest of the article if you want to check for code snippets for each of the above steps or download the plugin from here

Below are the code snippets for the steps mentioned above:
  1. Since creating a plugin project could be a long running process, it is better to run it in a background thread using IRunnableWithProgress:
  2. final IRunnableWithProgress runnableOp = new IRunnableWithProgress()
    {
    
      @Override
      public void run(final IProgressMonitor monitor) throws InvocationTargetException,
                                                               InterruptedException
      {
        try
        {
          monitor.beginTask("Creating a plugin project "
                                          + projectName, 2);
          IProject project = null;
          try
          {
            monitor.beginTask("", 10);
            monitor.subTask("Creating plugin project : "
                                            + projectName);
            
          } catch (final Exception e)
          {
          MessageDialog.openError(Display.getCurrent().getActiveShell(),
                                  "Error creating Project",
                                  "Error creating Project: \n" + e.getMessage());
          } finally
          {
            monitor.done();
          }
    
          monitor.worked(1);
        } finally
        {
          monitor.done();
        }
    }
    try
    {
      IRunnableContext context = PlatformUI.getWorkbench()
                                                     .getProgressService();
      context.run(false, true, runnableOp);
    } catch (InvocationTargetException | InterruptedException e)
    {
      MessageDialog.openError(Display.getCurrent().getActiveShell(),
                              "Creation problem",
                              "Project creation failed\n"+ e.getMessage());
    }
    
    
  3. Create a IJavaProject and attach IProjectDescription to it.
    final IWorkspace workspace = ResourcesPlugin.getWorkspace();
    project = workspace.getRoot().getProject(projectName);
    
    final IJavaProject javaProj = JavaCore.create(project);
    final IProjectDescription projDesc = ResourcesPlugin.getWorkspace().newProjectDescription(projectName);
    
    projDesc.setLocation(null);
    project.create(projDesc, monitor);
    
  4.  Set required Natures to the project
    private static void setNatures(final Set natureIdsSet,
                                       final IProjectDescription projDesc,
                                       boolean isMavenProject)
    {
       Set natureIds = new HashSet<>();
       natureIds.add(JavaCore.NATURE_ID);
       natureIds.add("org.eclipse.pde.PluginNature");
       if (isMavenProject)
       {
          natureIds.add("org.eclipse.m2e.core.maven2Nature");
       }
       natureIds.addAll(natureIdsSet);
    
       projDesc.setNatureIds(natureIds.toArray(new String[natureIds.size()]));
    }
    
  5.  Set required Builders to the project
    private static void setBuilders(final Set builderNamesSet,
                                        final IProjectDescription projDesc,
                                        boolean isMavenProject)
        {
            List builders = new ArrayList<>();
    
            final ICommand java = projDesc.newCommand();
            java.setBuilderName(JavaCore.BUILDER_ID);
            builders.add(java);
    
            final ICommand manifest = projDesc.newCommand();
            manifest.setBuilderName("org.eclipse.pde.ManifestBuilder");
            builders.add(manifest);
    
            final ICommand schema = projDesc.newCommand();
            schema.setBuilderName("org.eclipse.pde.SchemaBuilder");
            builders.add(schema);
    
            if (isMavenProject)
            {
                final ICommand mvn_schema = projDesc.newCommand();
                mvn_schema.setBuilderName("org.eclipse.m2e.core.maven2Builder");
                builders.add(mvn_schema);
            }
    
            for (String builderName : builderNamesSet)
            {
                final ICommand newBuilder = projDesc.newCommand();
                newBuilder.setBuilderName(builderName);
                builders.add(newBuilder);
            }
    
            projDesc.setBuildSpec(builders.toArray(new ICommand[builders.size()]));
        }
    
  6.  Open the project
    project.open(new SubProgressMonitor(monitor, 1));
                            project.setDescription(projDesc,
                            new SubProgressMonitor(monitor, 1));
    
  7.  Create required source folders and add classpath entries
    final List classpathEntries = new ArrayList();
    
    Collections.reverse(srcFolders);
    for (final String srcFolder : srcFolders)
    {
        final IFolder src = project.getFolder(srcFolder);
        if (!src.exists())
        {
           src.create(false, true, new SubProgressMonitor(monitor, 1));
        }
        final IClasspathEntry srcClasspathEntry = JavaCore.newSourceEntry(src.getFullPath());
        classpathEntries.add(0, srcClasspathEntry);
    }
    
    classpathEntries.addAll(Arrays.asList(NewJavaProjectPreferencePage.getDefaultJRELibrary()));
    classpathEntries.add(JavaCore.newContainerEntry(new Path("org.eclipse.pde.core.requiredPlugins")));
    
    if (isMavenProject)
    {
       final IClasspathEntry mavenDependencies = JavaCore.newContainerEntry(new Path("org.eclipse.m2e.MAVEN2_CLASSPATH_CONTAINER"), ClasspathEntry.NO_ACCESS_RULES, new IClasspathAttribute[] { new ClasspathAttribute("maven.pomderived", "true") }, false);
       classpathEntries.add(mavenDependencies);
    }
    
    javaProj.setRawClasspath(classpathEntries.toArray(new IClasspathEntry[classpathEntries.size()]),
                                                     new SubProgressMonitor(monitor, 1));
    
    
  8.  Set output location for the project
    javaProj.setOutputLocation(new Path("/" + projectName + "/bin"), new SubProgressMonitor(monitor, 1));
    
  9.  Create Manifest file
    private static void createManifest(final String projectName,
                                           final Set requiredBundles,
                                           final IProgressMonitor monitor,
                                           final IProject project) throws Exception
        {
            final StringBuilder maniContent = new StringBuilder("Manifest-Version: 1.0\n");
            maniContent.append("Bundle-ManifestVersion: 2\n");
            maniContent.append("Bundle-Name: " + projectName + "\n");
            maniContent.append("Bundle-SymbolicName: "
                               + projectName
                               + "; singleton:=true\n");
            maniContent.append("Bundle-Version: 1.0.0\n");
    
            if (!requiredBundles.isEmpty())
            {
                maniContent.append("Require-Bundle:");
                for (final Iterator iterator = requiredBundles.iterator(); iterator.hasNext();)
                {
                    maniContent.append(iterator.next());
                    if (iterator.hasNext())
                    {
                        maniContent.append(",\n");
                    } else
                    {
                        maniContent.append("\n");
                    }
                }
            }
    
            maniContent.append("Import-Package: org.osgi.framework\r\n");
            maniContent.append("Bundle-RequiredExecutionEnvironment: JavaSE-1.7\r\n");
            maniContent.append("Bundle-ActivationPolicy: lazy\r\n");
            maniContent.append("Bundle-Activator: org.eclipse.ppc.Activator\r\n");
    
            final IFolder metaInf = project.getFolder("META-INF");
            metaInf.create(false, true, new SubProgressMonitor(monitor, 1));
    
            FileGenerator.createFile("MANIFEST.MF",
                                     metaInf,
                                     maniContent.toString(),
                                     monitor);
        }
    
  10.  Create Build.props file
    private static void createBuildProps(final IProgressMonitor monitor,
                                             final IProject project,
                                             final List srcFolders) throws Exception
        {
            final StringBuilder bpContent = new StringBuilder("source.. = ");
            for (final Iterator iterator = srcFolders.iterator(); iterator.hasNext();)
            {
                bpContent.append(iterator.next()).append('/');
                if (iterator.hasNext())
                {
                    bpContent.append(",");
                }
            }
            bpContent.append("\n");
            bpContent.append("bin.includes = META-INF/,.\n");
            FileGenerator.createFile("build.properties",
                                     project,
                                     bpContent.toString(),
                                     monitor);
        }
    
  11.  Create an activator class for the project
    protected static void createJavaFiles(IProject pluginProject) throws Exception
        {
            String sourceFolderPath = "resrc/org/eclipse/ppc";
            FileGenerator.createFile("org/eclipse/ppc/Activator.java",
                                     pluginProject.getFolder("src"),
                                     FileGenerator.getContents(sourceFolderPath,
                                                               "Activator.java",
                                                               PluginProjectCreator.class),
                                     new NullProgressMonitor());
    
        }
    
    This method can be used to create any file in the destination plugin project by copying the content from inside the source plugin project. You might also want to replace some contents in the generated file (before writing into it), based on the values given in the wizard. 
Hope this helps!