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!

Tuesday, December 31, 2013

Reading files from inside a jar in classpath

Many times we depend on some thirdparty Jar files.

Here is the situation:
I created an eclipse plugin which helps a User to create Eclipse Plugin project easily. (I will be sharing this info as well in a separate blog).
When my plugin is loaded into an Eclipse, and when the user tries to execute an action that my plugin contributes, I need to copy a file which is packaged inside my plugin.jar and paste it into user's workspace.


Here is how I achieved it:

First find out  if my code being run is from local machine (Run as Eclipse application) or is it running as an Eclipse application; I do it using the code which I mentioned in my earlier blog entry.




private File getSourceProject()
    {
        final ProtectionDomain protectionDomain = this.getClass()
                                                      .getProtectionDomain();
        final CodeSource codeSource = protectionDomain.getCodeSource();
        final URL location = codeSource.getLocation();
        final File sourcePrj = new File(location.getFile());
        return sourcePrj;
    }



Then I create an InputStream object to the required file using the below code:



InputStream resourceStream;
        JarFile jar = null;
        if (sourcePrj.isFile())
        {
            // reading from plugin jar file
            jar = new JarFile(sourcePrj);
            final JarEntry resEntry = jar.getJarEntry(resourcePath);
            resourceStream = jar.getInputStream(resEntry);
        } else
        {
            // reading from source project
            resourceStream = new FileInputStream(sourcePrj.getAbsolutePath()
                                                 + "/"
                                                 + resourcePath);
        }

Here the resourcePath is the relative path to the required file inside the Jar.

Once we have the InputStream object, we can easily read it's contents and write it to some location, provided it is a text file and not a binary file.

I will be composing another blog entry on "Copying a Jar file programmatically"

Hope this helps.

Monday, June 18, 2012

Javascript Closures and few other Javascript concepts

Similar to Java's inner classes, JavaScript has something called Closures.

Before going into the details, let me explain the issue I was facing and why Closure was the right fit.

In my Java code, I had a native method like:

public native JavaScriptObject drawSomething()/*-{
//'menu' is variable to which we can add menu items
menu.addItem('Menu Item 1', 'someimage1', function() {
// Here goes the code which handles the event when 
//user clicks this menu item
});
}-*/;

In the above code, the method body is enclosed between /*-{ and }-*/. This marks the code as JSNI. JSNI is essentially more or less JavaScript code.

I had to add menuItems to the menu. In the above code (menu.addItem('Menu Item 1', 'someimage1', function()), the first parameter is the text to be displayed on the menu item, second parameter is the icon/image to be displayed for the menu item. Third parameter is a function definition. When a user clicks on this menu item, this function will be invoked.

I can even call like this:
menu.addItem('Menu Item 1', 'someimage1', myfunction);
function myfunction(){
//some code
}
where myfunction is the name of a function. That is, method-name can be passed as a parameter and JavaScript will invoke the method with that name.

Now I want to pass parameters to this new function.
Can I do like this:

menu.addItem('Menu Item 1', 'someimage1', myfunction('someString'));
function myfunction(str){
//some code
}
NO.
What happens when the above code is executed?
the myfunction(str) is invoked when the JavaScript engine executes the menu.addItem() method itself.
That is, it does not wait for the user to click on the menu item.

So how to pass variables to such methods?
Use Java Closures!

Solution is:
Move the menu.addItem() to a new method:
function myfunction(menu, str){
     function newfunction() {
          //Use the str parameter here
     }
menu.addItem(str, 'someimage1', newfunction);
}

In the above code, I wrote a new function named newmethod inside the myfunction().
This newmethod will have access to the parameters passed to its parent function, that is, myfunction. Here we can use the str parameter. The value for the str parameter was the 'Menu Item 1'.
That is because, I wanted to invoke a Java method with a parameter with value as the menuItem that has been selected by the user.

This is how we sue JavaScript closures.

Thanks to my colleagues Ganesh and Raj for helping me out solve an issue in Javascript using this concept.

Before I close, I would also like to mention about 'Invoking Java methods from inside JSNI' briefly:

function myfunction(menu, str){
     function newfunction() {
          //Use the str parameter here
          thisClass.@com.sample.MyClass::menuItemSelected(Ljava/lang/String;)(menuitem);

     }
menu.addItem(str, 'someimage1', newfunction);
}

In the above code, I am passing the text of the menuItem selected by the user to a instance method inside a Java class named MyClass in the package com.sample.

The syntax for invoking a Java method from JSNI is:
classobject.@fully_qualified_classname::method_name(syntax_of_parameter[s])(actual_parameter[s]);

Let me know if you have any questions.

Thursday, May 31, 2012

Amazon Interview Questions

I gave an interview with Amazon in the start of the year. Here are the questions I faced:

First Round was a Written Test.
Q1) Reverse words in a sentence
Q2) Print 2D array in Spiral Order
Q3) Remove all nodes with the given value in a linked list:
       LinkList removeValues(LinkList Head, ValueObject value){
       }
 Q4) Find the first non-repeating character in a word.

Second round was a F2F technical discussion:
Given a Tree:














What is the order of nodes when you parse the tree as:

1) Breadth First:
Ans: 1 2 3 4 5 6 7 8 9
2) Reverse Breadth First
Ans: 7 8 9 4 5 6 2 3 1
3) Zig Zag
Ans: 7 8 9 6 5 4 2 3 1

He asked many followup questions on Tree Parsing, searching, Collections, LinkedLists etc.
Few of them were like:
While doing the above Breadth First, etc, How would you do it if you were to do  only using:
1) Only two Queues
2) Only 1 Queue and 1 Stack
3) Only 2 Stacks

Third Round:
He gave me pattern like:
Write a recursive program to print similar pattern shown below given any number:
Pattern 1:
number: 5

-----

-
--
-

---

-
--
-

----

-
--
-

---

-
--
-

-----

Pattern 2:

number: 4

----

-
--
-

---

-
--
-

----

Pattern 3:
number: 6

------

-
--
-

---

-
--
-

----

-
--
-

-----

-
--
-

----

-
--
-

---

-
--
-

------

If you observe the patterns,

given a number the first and last lines will have so many number of dashes.

the the common pattern occurs between dash lines:
-
--
-

Then after the first line, start with 3 dashes and go till (n-1) then go back till 3. 

Friday, March 9, 2012

Solution for the "Issue with Executable/Runnable Jar file"

I was facing an issue when running an Executable Jar file.:

I had a simple Java Project, in which I had a class named "Sample.java". I need to use log4j for logging statements. This project should be archived as an Executable Jar.

My Project has the following files:











The Sample.java looks like:
package com.dwij.sample;

import org.apache.log4j.Logger;

public class Sample {

 public static final Logger LOGGER = Logger.getLogger(Sample.class);

 public static void main(String[] args) {
  LOGGER.info("Simple usage of log4j");
 }
}


You can download the zip file of this project from here

In the Manifest.MF file, mention your Main class and classpath:
Manifest-Version: 1.0
Class-Path: lib/log4j.jar
Main-Class: com.dwij.sample.Sample



Now I go to command prompt and issue commands for:
  1. Compile sources: javac -sourcepath src -classpath classes;lib\log4j.jar src\com\dwij\sample\*.java -d classes
  2. Archive the project into a Jar file: jar -cvfm Sample.jar Manifest.MF -C lib . -C classes . .classpath .project
  3. Now run the jar file: java -jar Sample.jar. (Issue this command after placing the Sample.jar in a separate folder)
    • This fails with an error: NoClassDefFoundError for the org.apache.log4j.Logger class
Why is so?
That is because, When you have your main class inside a Jar file and if you need to refer to classes from the Jar files from this main class, which are again packaged inside the same Jar, YOU JUST CAN'T DO IT.
There is a thread which explains this issue: Executable JAR ClassPath problem

Solution 1:
You will be able to run this only when the Sample.jar is sitting beside a folder named "lib", which has the log4j.jar.

That is because, when you run the Executable Jar, the Manifest.MF is read for the classpath property. In our case, we mentioned lib\log4j.jar. So it looks for a "lib" folder at the same level where the jar resides.
But this is not an optimal solution, ideally, we would like to package our code in a single self-contained Jar file.

Solution2:
Here, the requirement is to package all the dependent jars also inside the Sample.jar.
For this, I used Eclipse's solution. When you export a Java Project from Eclipse, and mark it as Runnable Jar specifying your Main class, all the dependent Jar files will be packaged into the resultant Runnable Jar file and there are 5 classes that are added by Eclipse. Apart from these classes, Eclipse modifies the Manifest.MF file to add the following parameters:

Rsrc-Class-Path: ./ log4j.jar
Class-Path: .
Rsrc-Main-Class: com.dwij.sample.Sample
Main-Class: org.eclipse.jdt.internal.jarinjarloader.JarRsrcLoader

Here, Eclipse is using the concept of URL Class loaders. This manifest file will be read and your actual Main class will be called after pointing the classpath to the Jars files mentioned in the "Rsrc-Class-Path" property.

I included these classes in my source project, compiled and archived.

That works like a charm!

Here are the steps:
  • Copy the following classes into your source project:
Now go to command prompt and issue commands for:
  • Compile sources: javac -sourcepath src -classpath classes;lib\log4j.jar src\com\dwij\sample\*.java src\org\eclipse\jdt\internal\jarinjarloader\*.java -d classes
    • Observe that we are compiling the new package as well.
  • Archive the project into a Jar file: jar -cvfm Sample.jar Manifest.MF -C lib . -C classes . .classpath .project
  • Now run the jar file: java -jar Sample.jar. (Issue this command after placing the Sample.jar in a separate folder)
  • Observe that the Jar runs fine! 

Happy executing-your-Runnable-Jar-files! :)

Thursday, March 8, 2012

Using Log4J

In this post, I will try to keep things as simple as possible and explain how to use log4j in Java.

1. Create a Java Project in Eclipse, say "Sample"
2. Create a java class with main() method, say Sample.java.
3. Download log4j.jar. I would recommend you to download the latest jar.
4. Have that log4j.jar in buildpath of the project.
5. In the Sample.java, write the following code to include Logging statements:
package com.dwij.sample;

import org.apache.log4j.Logger;

public class Sample {

 public static final Logger LOGGER = Logger.getLogger(Sample.class);

 public static void main(String[] args) {
  LOGGER.info("Simple usage of log4j");
 }
}

6. Run the Sample.java class. Yes, the error you see is expected.
log4j:WARN No appenders could be found for logger (com.dwij.sample.Sample).
log4j:WARN Please initialize the log4j system properly.

7. How to fix this "No appenders issue"?
  • Have a log4j.properties in classpath of the project:
    • Create a file with name "log4j.properties" and have the below content in it:
      • log4j.rootCategory=DEBUG, S
        log4j.appender.S = org.apache.log4j.ConsoleAppender
        log4j.appender.S.layout = org.apache.log4j.PatternLayout
        log4j.appender.S.layout.ConversionPattern = %c{1} [%p] %m%n
    • The logging level can be DEBUG, INFO, ERROR, FATAL
    • "S" in log4j.rootCategory can be any string you want; it is just that you need to give the same string in the other properties (log4j.appender.S, log4j.appender.S.layout, log4j.appender.S.layout.ConversionPattern)
    • I am using ConsoleAppender here because I just want to see the Log statements in the console.
    • ConversionPattern goes something like this:
      • %c{1}  prints the classname that is ran
      • [%p] prints the Log level
      • %m prints the message provided in the log statement in the code
      • %n prints a new line
  • Define your own appender and add it to the Logger instance:
package com.dwij.sample;

import org.apache.log4j.Appender;
import org.apache.log4j.ConsoleAppender;
import org.apache.log4j.Level;
import org.apache.log4j.Logger;
import org.apache.log4j.SimpleLayout;

public class Sample {
 
 static Appender myAppender;  
 public static final Logger LOGGER = Logger.getLogger(Sample.class);

 public static void main(String[] args) {
  // Define Appender     
  myAppender = new ConsoleAppender(new SimpleLayout());  
  
  LOGGER.addAppender(myAppender);  
  LOGGER.setLevel(Level.ALL); 
 
  LOGGER.info("Simple usage of log4j");
 }
}

8. Run the class, and you should see the below output:
INFO - Simple usage of log4j

In order to keep things simple, I have not included examples which include writing Log statements to Files, etc.

I will add more content, if requested.

Monday, September 5, 2011

From which Jar is my class loaded?

Ever wondered from which Jar is a class loaded at runtime?
I am talking about class loading here. That is, if there is a class (say, com.test.Class1)  which is in MyJar1.jar and MyJar2.jar. Each of these Jars are different classpath levels.

Now in your class, execute the below code and you can see the complete path to the Jar file from which the Class is loaded:


CodeSource resource = WSDLFactory.class.getProtectionDomain().getCodeSource(); if (resource != null)      System.out.println("The URL --> " + resource.getLocation());


I used above code to know from which wsdl4j.jar did the WSDLFactory class got loaded. I had wsdl4j.jar in 3 places, each was of different version. I had to identify the jar which was used at runtime. Thanks to my lead to teach me this :)