Tuesday, July 2, 2013

Writing your own JCA extensions - a full cipher

So you have managed to create your own cipher. Great! Now lets go for the real thing and implement a cipher.

Basically ciphers are implemented similar to digests. But while digests can be used without any security restrictions, ciphers need to be signed when used with JCA. In more detail your cipher implementation must reside in a signed jar file. To sign you need a code signing certificate from Oracle. Application for such a certificate is free of charge, but will take approximately 2 weeks to be processed. You will not get it for fun either as you have to provide your use case.

For the exact procedure follow Oracles step by step guide. To follow the tutorial below I expect you to have your certificate ready. Otherwise you might do the coding but will not be able to use your provider afterwards.

Source code for this tutorial is available on googlecode as a single zip archive, as a Team Project Set or you can checkout the SVN projects directly.

Step 1: Create key classes

We wil create a Caesar cipher implementation. You could also do ROT13 with it. As ciphers are about encryption/decryption we need some keys. Our key class will be very simple as we just need to store an offset for shifting our letters later on.

Start by creating a new Java Project named com.example.jce.cipher. Then implement the key class com.example.jce.provider.CaesarKey:
package com.example.jce.cipher;

import javax.crypto.SecretKey;

public class CaesarKey implements SecretKey {
 private static final long serialVersionUID = -542084808194990775L;

 private byte mValue;

 public CaesarKey(byte value) {
  // converts any value to [0, 26), even negative ones
  mValue = (byte) (((value % 26) + 26) % 26);
 }

 @Override
 public String getAlgorithm() {
  return "Caesar";
 }

 @Override
 public String getFormat() {
  return getAlgorithm() + " format";
 }

 @Override
 public byte[] getEncoded() {
  return new byte[] { mValue };
 }
}
You may also implement a key generator that creates random keys on demand. This is not mandatory for our example but may be useful sometimes. The generator is implemented in com.example.jce.cipher.CaesarkeyGenerator:
package com.example.jce.cipher;

import java.security.InvalidAlgorithmParameterException;
import java.security.SecureRandom;
import java.security.spec.AlgorithmParameterSpec;

import javax.crypto.KeyGeneratorSpi;
import javax.crypto.SecretKey;

public class CaesarKeyGenerator extends KeyGeneratorSpi {

 SecureRandom mRandomSource;

 public void engineInit(SecureRandom sr) {
  mRandomSource = sr;
 }

 public void engineInit(AlgorithmParameterSpec ap, SecureRandom sr) throws InvalidAlgorithmParameterException {
  throw new InvalidAlgorithmParameterException("No parameters supported in this class");
 }

 public SecretKey engineGenerateKey() {
  if (mRandomSource == null)
   mRandomSource = new SecureRandom();

  return new CaesarKey((byte) Math.abs(mRandomSource.nextInt()));
 }

 @Override
 protected void engineInit(int arg0, SecureRandom sr) {
  engineInit(sr);
 }
}
Step 2: Implement the cipher

Ciphers are implemented extending CipherSpi (as all JCA extensions extend a WhateverSpi class). Implement com.example.jce.cipher.CaesarCipher as follows:
package com.example.jce.cipher;

import java.security.AlgorithmParameters;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.Key;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.security.spec.AlgorithmParameterSpec;

import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.CipherSpi;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.ShortBufferException;

public class CaesarCipher extends CipherSpi {

 private Key mKey;
 private int mOPMode;

 @Override
 protected byte[] engineDoFinal(byte[] input, int inputOffset, int inputLen) throws IllegalBlockSizeException, BadPaddingException {
  return engineUpdate(input, inputOffset, inputLen);
 }

 @Override
 protected int engineDoFinal(byte[] input, int inputOffset, int inputLen, byte[] output, int outputOffset) throws ShortBufferException,
   IllegalBlockSizeException, BadPaddingException {
  return engineUpdate(input, inputOffset, inputLen, output, outputOffset);
 }

 @Override
 protected int engineGetBlockSize() {
  return 1;
 }

 @Override
 protected byte[] engineGetIV() {
  return null;
 }

 @Override
 protected int engineGetOutputSize(int inputLen) {
  return 0;
 }

 @Override
 protected AlgorithmParameters engineGetParameters() {
  return null;
 }

 @Override
 protected void engineInit(int opmode, Key key, SecureRandom random) throws InvalidKeyException {
  mOPMode = opmode;

  if (key instanceof CaesarKey)
   mKey = key;

  else
   throw new InvalidKeyException("Caesar cipher needs a Caesar key");
 }

 @Override
 protected void engineInit(int opmode, Key key, AlgorithmParameterSpec params, SecureRandom random) throws InvalidKeyException,
   InvalidAlgorithmParameterException {
  engineInit(opmode, key, random);
 }

 @Override
 protected void engineInit(int opmode, Key key, AlgorithmParameters params, SecureRandom random) throws InvalidKeyException,
   InvalidAlgorithmParameterException {
  engineInit(opmode, key, random);
 }

 @Override
 protected void engineSetMode(String mode) throws NoSuchAlgorithmException {
  throw new NoSuchAlgorithmException();
 }

 @Override
 protected void engineSetPadding(String pading) throws NoSuchPaddingException {
  throw new NoSuchPaddingException();
 }

 @Override
 protected byte[] engineUpdate(byte[] input, int inputOffset, int inputLen) {
  byte[] output = new byte[inputLen];
  try {
   engineUpdate(input, inputOffset, inputLen, output, 0);
  } catch (ShortBufferException e) {
   // output buffer is always big enough
  }

  return output;
 }

 @Override
 protected int engineUpdate(byte[] input, int inputOffset, int inputLen, byte[] output, int outputOffset) throws ShortBufferException {
  if (output.length - outputOffset < inputLen)
   throw new ShortBufferException("Buffer too short to hold encrypted data");

  for (int index = inputOffset; index < inputOffset + inputLen; index++)
   output[outputOffset + index - inputOffset] = twistCharacter(input[index], getKey());

  return inputLen;
 }

 private byte getKey() {
  // calculate key depending on encryption/decryption
  if (mOPMode == Cipher.ENCRYPT_MODE)
   return mKey.getEncoded()[0];

  // decode using inverse key
  return (byte) (26 - mKey.getEncoded()[0]);
 }

 private byte twistCharacter(byte data, byte key) {

  if (Character.isLetter(data)) {
   // only operate on characters, leave everything else as-is
   if (Character.isLowerCase(data))
    return (byte) (((data - 'a' + key) % 26) + 'a');
   else
    return (byte) (((data - 'A' + key) % 26) + 'A');
  }

  return data;
 }

 @Override
 protected int engineGetKeySize(Key key) throws InvalidKeyException {
  if (key instanceof CaesarKey)
   return 1;

  throw new InvalidKeyException("Caesar cipher needs a Caesar key");
 }
}
This is a straight forward implementation that will encode characters only. Everything else like digits or punctuation will remain unchanged.

Step 3: Create a JCE provider

As for the digest, we need to usa a provider to register our digest in the JRE. Create a new class com.example.jce.cipher.CaesarProvider:
package com.example.jce.cipher;

import java.security.Provider;

public class CaesarProvider extends Provider {

 private static final long serialVersionUID = -67123468605911408L;

 public CaesarProvider() {
  super("Caesar Cipher", 1.0, "Caesar cipher v1.0");

  put("KeyGenerator.Caesar", CaesarKeyGenerator.class.getName());
  put("Cipher.Caesar", CaesarCipher.class.getName());
 }
}
At this point we need the certificate requested from Oracle, because a cipher class needs to be nested in a signed jar file. If you do not have such a certificate your quest ends here...

For all the others: export the project as a JAR file and sign it using jarsigner along with your certificate. The commandline looks something like this:
jarsigner -keystore <store location> -storepass <store master password> <JAR file> <keystore alias>

Step 4: Using our provider

Create a new Java project com.example.jce.cipher.consumer. Create a subfolder libs and store your signed JAR file there. Then add this JAR to the Java Build Path.

Now everything is in place to use our cipher:
 public static void main(String[] args) {
  // adding provider dynamically
  Security.addProvider(new CaesarProvider());

  try {
   String message = "Hello World";
   Cipher c = Cipher.getInstance("Caesar");
   SecretKey key = new CaesarKey((byte) 2);
   System.out.println("Message:   " + message);

   c.init(Cipher.ENCRYPT_MODE, key);
   byte[] encrypted = c.doFinal(message.getBytes());
   System.out.println("Encrypted: " + new String(encrypted));

   c.init(Cipher.DECRYPT_MODE, key);
   byte[] decrypted = c.doFinal(encrypted);
   System.out.println("Decrypted: " + new String(decrypted));

  } catch (Exception e) {
   e.printStackTrace();
  }
 }

Further reading

If you intend to implement your own stuff you should have a look at these links:

Wednesday, June 26, 2013

Writing your own JCA extensions - a simple digest

When dealing with cryptographic algorithms in Java you should use the Java Cryptography Architecture (JCA). It is part of Java SE and provides support for several different algorithms like DES or AES. Even more algorithms are supported by BouncyCastle.

But what if you need to implement your own digest or cipher? JCE allows to add your own cryptographic provider to the framework, which is what we will do in this tutorial.

Source code for this tutorial is available on googlecode as a single zip archive, as a Team Project Set or you can checkout the SVN projects directly.

Step 1: Create a digest implementation

We will implement a simple digest that will XOR all bytes of a message and therefore return a 1 byte checksum.

Start by creating a new Java Project named com.example.jce.provider. To implement a digest we need to extend MessageDigestSpi. Implement com.example.jce.provider.XORDigest as follows:
package com.example.jce.provider;

import java.security.MessageDigestSpi;

public class XORDigest extends MessageDigestSpi {

 private byte mChecksum = 0;

 public XORDigest() {
 }

 @Override
 protected void engineUpdate(byte input) {
  mChecksum ^= input;
 }

 @Override
 protected void engineUpdate(byte[] input, int offset, int len) {
  for (int index = offset; index < offset + len; index++)
   engineUpdate(input[index]);
 }

 @Override
 protected byte[] engineDigest() {
  return new byte[] { mChecksum };
 }

 @Override
 protected void engineReset() {
  mChecksum = 0;
 }
}
Step 2: Create a JCE provider

To register our digest in the JRE we need to use a provider. This provider is used by the JCE as an entry point to locate our Digest class. Create a new class com.example.jce.provider.CryptoProvider:
package com.example.jce.provider;

import java.security.Provider;

public class CryptoProvider extends Provider {

 private static final long serialVersionUID = -67123468605911408L;

 public CryptoProvider() {
  super("SimpleCrypto", 1.0, "XOR digest v1.0");

  put("MessageDigest.XOR", XORDigest.class.getName());
 }
}
Finally we need to register this provider in our runtime. There are several ways to do this, here we will use the dynamic approach as we do not want to alter our JRE install folder. Registration is handled by the Security class:
Security.addProvider(new CryptoProvider());

Step 3: Using our provider

Lets see our provider in action:
 public static void main(String[] args) {

  Security.addProvider(new CryptoProvider());

  try {
   MessageDigest digest = MessageDigest.getInstance("XOR");
   byte[] checksum = digest.digest(new byte[] { 0x01, 0x23, 0x45 });
   System.out.println("Checksum: 0x" + Integer.toHexString(checksum[0]));

  } catch (NoSuchAlgorithmException e) {
   e.printStackTrace();
  }
 }
First we are registering our provider as explained in step 2. Afterwards we create a new digest and feed some data to it. The checksum you get should be 0x67.

Thursday, June 13, 2013

Using ANTLR in your RCP

ANTLR is a parser generator widely used to create DSLs and all kinds of parsers for special purposes. In this tutorial I will show how to create a simple parser and how to implement it into an RCP.

Source code for this tutorial is available on googlecode as a single zip archive, as a Team Project Set or you can checkout the SVN projects directly.

Prerequisites

We need some stuff from the ANTLR download page. Get Complete ANTLR 4.0 Java binaries jar and ANTLR 4.0 Java runtime binaries jar from there. The full package includes the build tool for grammars, the runtime binaries are needed to execute a parser in our RCP later on.

Step 1: Create a simple grammar

As this tutorial is not about ANTLR itself, but its integration into an RCP, we will use a very simple grammar to parse CSV files (actually just a single line of separated integers).

Currently there seems to be no eclipse support for ANTLRv4. To write grammars you could use ANTLRWorks 2 or write it directly in an editor of your choice.

Create a new Plug-in Project named com.example.antlr.parser. Afterwards create a grammar file CSV.g4 in the project root and add following content:
grammar CSV;

csv: NUMBER (DELIMITER NUMBER)*;

DELIMITER: ',';
NUMBER: DIGIT+;

fragment
DIGIT: [0-9];
Step 2: Compile grammar

To compile our grammar file we need the Complete ANTLR package we downloaded earlier and a simple script. As there is no native eclipse builder available, we will use an external build script.

Create a new folder build and copy antlr-4.0-complete.jar to it. Afterwards create a new file build.bat with following content:
"C:\Program Files\Java\jdk1.7.0_04\bin\java.exe" -cp antlr-4.0-complete.jar org.antlr.v4.Tool -visitor -package com.example.antlr.gen -o ..\src\com\example\antlr\gen\foo ..\CSV.g4

Extend this to your needs, port it to ANT or integrate it in a maven build (there seems to exist a maven target for ANTLRv4). It seems that the -o parameter needs some dummy folder name at the end. At least for me the very last path entry was skipped by the build tool.

You may launch this batch file from the External Tools located in the Run menu. The launcher is available in the source repository in the build folder.


Step 3: Add ANTLR runtime

The created files show lots of error markers because we have no runtime available yet. Create a new Plug-in from Existing JAR Archives and add antlr-runtime-4.0.jar to it. Name the project org.antlr.v4.runtime and add it as a dependency to com.example.antlr.parser.  All the error markers should be gone by now.

ANTLR comes with a BSD like license so you have to check if this fits to your application needs. According to this wiki entry it seems that it fits to EPL.

Step 4: Implement the parser

To run the parser we need 2 more classes. Lets start with com.example.antlr.CSVVisitor
package com.example.antlr;

import java.util.Arrays;
import java.util.Collections;
import java.util.LinkedList;
import java.util.List;

import org.antlr.v4.runtime.tree.RuleNode;
import org.antlr.v4.runtime.tree.TerminalNode;

import com.example.antlr.gen.CSVBaseVisitor;
import com.example.antlr.gen.CSVLexer;

public class CSVVisitor extends CSVBaseVisitor<List<Integer>> {

 @Override
 public List<Integer> visitChildren(RuleNode ctx) {
  List<Integer> numbers = new LinkedList<Integer>();
  for (int index = 0; index < ctx.getChildCount(); index++)
   numbers.addAll(visit(ctx.getChild(index)));

  return numbers;
 }

 @Override
 public List<Integer> visitTerminal(TerminalNode node) {

  if (node.getSymbol().getType() == CSVLexer.NUMBER) {
   int number = Integer.parseInt(node.getText());
   return Arrays.asList(number);
  }

  return Collections.emptyList();
 }
}
Now we can finally call the parser from com.example.antlr.CSVProcessor
package com.example.antlr;

import java.util.List;

import org.antlr.v4.runtime.ANTLRInputStream;
import org.antlr.v4.runtime.CommonTokenStream;

import com.example.antlr.gen.CSVLexer;
import com.example.antlr.gen.CSVParser;

public class CSVProcessor {

 public static List<Integer> parseCSV(final String data) {
  CSVLexer lexer = new CSVLexer(new ANTLRInputStream(data));
  CSVParser parser = new CSVParser(new CommonTokenStream(lexer));

  return parser.csv().accept(new CSVVisitor());
    }
 
 public static void main(String[] args) {
  List<Integer> numbers = parseCSV("2,45,66,123,24,6,7,7,8");
  System.out.println(numbers);
 }
}
If you need support on ANTLRv4 check out the webpage or - even better - support the project and buy the book.

Monday, April 22, 2013

Re-using core expressions for own extension points

Most of us use the expression framework to enable/disable handlers or to display/hide UI elements by providing enabledWhen or visibleWhen statements. Recently I needed to define an enablement for a custom extension point and found it appealing to reuse the expression framework for that purpose.

Source code for this tutorial is available on googlecode as a single zip archive, as a Team Project Set or you can checkout the SVN projects directly.
 
Step 1: Create an extension point

Create a new Plug-in project com.example.expressions. Switch to the Extension Points tab and create a new extension point by clicking the Add... button.

Set Extension Point ID to sample, Extension Point Name to Sample and hit Finish. We end up in the extension point schema editor where we switch to the Definition tab.

Create a baseElement and an expression element (hit New Element for that purpose). The baseElement contains a sequence (or choice, doesn't matter) of expressions. Make sure that exactly one expression can be defined by setting Max Occurrences to 1. If you set Min Occurrences to 0 the expression is optional, otherwise it is mandatory.

An expression is constructed by elements of certain types. When you are familiar with enabledWhen, visibleWhen, ... you are already familiar with these. They are defined in another scheme which we need to reference from our custom theme. Unless bug 384001 is fixed we need to do this in the Source view of our extension. Add following line within the schema node of your extension source:
<include schemaLocation="schema://org.eclipse.core.expressions/schema/expressionLanguage.exsd"/>
Now switch back to the Definition tab and add a choice of following elements to the expression element:

not, or, and, instanceof, test, systemTest, equals, count, with, resolve, adapt, iterate, reference.

Finally add a choice (or sequence) of baseElements to the extension element. Your assembled definition should look like this:


Here is the xml definition for your reference:
<?xml version='1.0' encoding='UTF-8'?>
<!-- Schema file written by PDE -->
<schema targetNamespace="com.example.expressions" xmlns="http://www.w3.org/2001/XMLSchema">
<annotation>
      <appinfo>
         <meta.schema plugin="com.example.expressions" id="sample" name="Sample"/>
      </appinfo>
      <documentation>
         [Enter description of this extension point.]
      </documentation>
   </annotation>

   <include schemaLocation="schema://org.eclipse.core.expressions/schema/expressionLanguage.exsd"/>

   <element name="extension">
      <annotation>
         <appinfo>
            <meta.element />
         </appinfo>
      </annotation>
      <complexType>
         <choice minOccurs="1" maxOccurs="unbounded">
            <element ref="baseElement"/>
         </choice>
         <attribute name="point" type="string" use="required">
            <annotation>
               <documentation>
                  
               </documentation>
            </annotation>
         </attribute>
         <attribute name="id" type="string">
            <annotation>
               <documentation>
                  
               </documentation>
            </annotation>
         </attribute>
         <attribute name="name" type="string">
            <annotation>
               <documentation>
                  
               </documentation>
               <appinfo>
                  <meta.attribute translatable="true"/>
               </appinfo>
            </annotation>
         </attribute>
      </complexType>
   </element>

   <element name="baseElement">
      <complexType>
         <sequence>
            <element ref="expression"/>
         </sequence>
      </complexType>
   </element>

   <element name="expression">
      <complexType>
         <choice>
            <element ref="not"/>
            <element ref="or"/>
            <element ref="and"/>
            <element ref="instanceof"/>
            <element ref="test"/>
            <element ref="systemTest"/>
            <element ref="equals"/>
            <element ref="count"/>
            <element ref="with"/>
            <element ref="resolve"/>
            <element ref="adapt"/>
            <element ref="iterate"/>
            <element ref="reference"/>
         </choice>
      </complexType>
   </element>

   <annotation>
      <appinfo>
         <meta.section type="since"/>
      </appinfo>
      <documentation>
         [Enter the first release in which this extension point appears.]
      </documentation>
   </annotation>

   <annotation>
      <appinfo>
         <meta.section type="examples"/>
      </appinfo>
      <documentation>
         [Enter extension point usage example here.]
      </documentation>
   </annotation>

   <annotation>
      <appinfo>
         <meta.section type="apiinfo"/>
      </appinfo>
      <documentation>
         [Enter API information here.]
      </documentation>
   </annotation>

   <annotation>
      <appinfo>
         <meta.section type="implementation"/>
      </appinfo>
      <documentation>
         [Enter information about supplied implementation of this extension point.]
      </documentation>
   </annotation>
</schema>

Step 2: Using the extension point

Go back to the plugin.xml of com.example.expressions and switch to the Extensions tab. Add a new extension of type com.example.expressions.sample. Now add a baseElement to it and fill the expression child node with some useful content:


This simple expression example expects the current selection to be empty.

I am using the org.eclipse.ui.startup extension point to create some sample test code to evaluate the expression.

Step 3: Create Java Expression from plugin definition

When parsing extension points data to java code, we need to convert the expression definition to a java org.eclipse.core.expressions.Expression instance. The ExpressionConverter class will do that for us:
private static final String PLUGIN_ID = "com.example.expressions";
private static final String EXTENSION_POINT = "sample";
private static final String NODE_EXPRESSION = "expression";

public Expression loadExpression() {
 IConfigurationElement[] elements = Platform.getExtensionRegistry().getExtensionPoint(PLUGIN_ID, EXTENSION_POINT).getConfigurationElements();
 for (IConfigurationElement element : elements) {

  // find expression nodes
  final IConfigurationElement[] children = element.getChildren(NODE_EXPRESSION);
  if (children.length == 1) {
   // we expect exactly 1 expression node...

   final IConfigurationElement[] expressionRoot = children[0].getChildren();
   if (expressionRoot.length > 0) {
    // ... containing exactly one root entry
    try {
     return ExpressionConverter.getDefault().perform(expressionRoot[0]);

    } catch (final CoreException e1) {
    }
   }
  }
 }

 return null;
}

Step 4: Evaluating an expression

For evaluation an expression needs an IEvaluationContext. A context contains variables (which can be used by a with node of an expression) to be evaluated. If you do not need variables you can simply create a new EvaluationContext and add your own variables to it:
EvaluationContext context = new EvaluationContext(null, new Object());
context.addVariable("someName","some arbitrary content");
EvaluationContext takes as first argument a base context (which may be null) and as second argument a default variable to perform the test upon (used when no when node exists in the expression).
You can also use the default context used by the workbench. Get it from the IHandlerService:
IHandlerService service = (IHandlerService) PlatformUI.getWorkbench().getService(IHandlerService.class);
IEvaluationContext currentState = service.getCurrentState();
Now evaluating the expression works as follows:
public Object evaluateExpression(Expression expression) throws CoreException {
 IHandlerService service = (IHandlerService) PlatformUI.getWorkbench().getService(IHandlerService.class);
 IEvaluationContext currentState = service.getCurrentState();

 return expression.evaluate(currentState);
}
The sample project contains running code to see this expression in action.

References

Eclipse API documentation

Thursday, April 4, 2013

Adding JavaDoc to help system

Sometimes it is useful to provide JavaDoc documentation to the user. While adding such documentation to an RCP application might be a special case it could make more sense to have such documentation ready for Eclipse extensions. If you combine this with custom doclets you might generate even more than just API documentation.

Instead of adding external html files we will add this documentation to the Eclipse help system.

Source code for this tutorial is available on googlecode as a single zip archive, as a Team Project Set or you can checkout the SVN projects directly.

Step 1: Create JavaDoc

First lets create a place to store our generated files in. So create a new Plug-in Project called com.example.javadoc.

Now select the menu entry Project / Generate Javadoc....

You need to define the javadoc executable, the source files to be parsed and the destination where to create html files. We will set the latter to com.example.javadoc/javadoc.


On the last wizard page you have the option to save all settings to an ant script so you can regenerate your documentation easily.

 

After finishing the wizard you are asked whether to update the javadoc location or not. Say No To All here.

When you refresh your project in the navigator you should see the generated files.

Step 2: Optimize build process

Refreshing by hand is nasty, why not let Eclipse do the job for us? Select your generate_javadoc.xml, right click and select Run As / Ant Build ... On the Refresh tab you can adjust which resources to refresh after the build process.

Step 3: Adding files to Eclipse help

There exist lots of tutorials on how to add help content to eclipse. As a starting point take a look at Contributing a little help or Lars' tutorial on Adding Help to RCP Applications.

We will create a book with a single section containing our help content. Create the book by adding a new file help/book.xml to our project:
<?xml version="1.0" encoding="UTF-8"?>
<toc label="Sample Book">
 <topic label="API Documentation">
  <link toc="help/API_documentation.xml" />
 </topic>
</toc>
Now add another file help/API_documentation.xml:
<?xml version="1.0" encoding="UTF-8"?>
<toc label="Sample API">
 <topic label="JavaDoc" href="javadoc/index.html" />
</toc>

You can open/edit these files with the Table of Contents Editor, which gives you a nicer UI to create such files, but usually the XML is simple enough to remain in the text editor.

To add these files to Eclipse help we need to use extension points. Open your MANIFEST.MF file and switch to the Extensions tab. Create a new extension for org.eclipse.help.toc and create two toc child nodes. Each of them refers to one of our xml files. To create a new book (which is displayed as a root level element in the help contents) we need to set primary to true.


The final plugin.xml should look like this:
<?xml version="1.0" encoding="UTF-8"?>
<?eclipse version="3.4"?>
<plugin>
   <extension
         point="org.eclipse.help.toc">
      <toc
            file="help/book.xml"
            primary="true">
      </toc>
      <toc
            file="help/API_documentation.xml"
            primary="false">
      </toc>
   </extension>
</plugin>
Finally switch to the Build tab and make sure help and javadoc folders are included in the Binary Build.

Run your application and select Help / Help Contents to see our new book in action.

Optional: Open a help page programmatically

The help system allows to open dedicated pages on demand. You can open an html page by calling
PlatformUI.getWorkbench().getHelpSystem().displayHelpResource("/<plugin ID>/path/to/your/file.html");

Further improvements

Currently the creation of JavaDoc is still a manual process. But we could combine this with the Custom Builder tutorial and let the builder run javadoc. Then you could add the builder to all your projects that contribute to your javadoc files.

Wednesday, February 27, 2013

Enhance your navigator by decorating resources

Is your project explorer stuffed with too many projects? Are you looking for a way to get more structure into this? I was suffering from this problem for a long time now, having workspaces with 50+ projects in it. I am using Working Sets and Mylyn, still I sometimes get lost within all my resources.

Resource Decorator Plugin

So I came up with a nice little plugin that allows to add custom decorations to your resources. Eg you could set a dedicated background color for all your projects that fit to a dedicated name pattern. You could also set the foreground color for all your src folders or highlight plugin.xml files.


The Resource Decoration is accessible through the Navigator context menu, which contains some quick decorations and a generic wizard that allows to fine tune your settings. There you can build generic filters by creating a tree structure. The Resource filter will match a resource path within your workspace. This will always be an exact match. The RegExp filter takes a regular expression to match it against the resource name. You have to use drag and drop to build your filters, DEL will remove a selected filter along with its children.


To alter decorations you can go to Preferences / General / Appearance / Resource Decorator. Take care that the Resource Decorator is enabled in Preferences / General / Appearance / Label Decorations (which should be the default setting).


The tool is at a very early stage as the interface is a little rough and filtering is a bit limited so far. Yet it is working, save and quite useful to me.

Installation

When you are interested in this little helper you can install it from

http://codeandme.googlecode.com/svn/trunk/at.pontesegger/at.pontesegger.resourcedecorator.releng.p2/target/repository

Please use the bugtracker at googlecode to report any issues or additional ideas.

The whole project is available under EPL, source code is located at Google code.

Tuesday, February 19, 2013

Using git flow in eclipse

Git flow is a great way to improve your development process. It helps in setting up dedicated branches for development, releases and fixes. If you are not familiar with git flow please read a successful git branching model before continuing. Git flow is extremely easy to use and really worth a try.

Git flow is currently not integrated in eclipse (see bug 348610), so we need to use a command line to use it. Basically I love the power of command lines but when using an IDE I prefer visual support, so lets find a way to make this a bit more user friendly.

For this tutorial I assume you are using windows (if you were on linux you would love your bash anyway).

Step 1: Installing msysgit

As we need to enter commands on the command line we need to install the necessary tools for that, no way to avoid that.

Download and install the latest version of Git. At the time of writing this is v1.8.1.2. Go with the default settings when asked for the components and select Run Git from the Windows Command Prompt when asked for Adjusting your PATH environment. Line endings can be configured as you prefer.

You need to log off so your PATH variable will be updated accordingly. Afterwards you should be able to use the git command in a console.

Step 2: Installing git flow

Now we need to install the git flow addon. Here is a short excerpt of the install wiki page:

We need 2 additional files to download and copy to our msysgit/bin folder:
Go to the util-linux-ng download site and download the Binaries zip. From there extract only getopt.exe. Then download Dependencies zip to extraxt libintl3.dll.

During the setup we need to create a temporary directory. Open a command shell and switch to your temp directory
cd temp
git clone --recursive git://github.com/nvie/gitflow.git
cd gitflow
contrib\msysgit-install.cmd
The install command should work if you are using the default install locatin for msysgit. If it still cannot find msysgit you have to provide the install location as a parameter.

Afterwards try to run gitflow by entering
git flow
in a shell. If it works you can safely remove your temporary install folder.

Step 3: Using git flow

We are ready to use git flow by now from our shell. So give it a try, best following the blog post by Jeff Kreeftmeijer: Why aren't you using git-flow?

Step 4: Adding a shell to eclipse

WickedShell is a nice plugin for eclipse that integrates a shell into the IDE. Install it and restart your IDE. Now you can open a new view Wicked Shell / Shell which basically gives you a command prompt on windows. While you can use the windows shell I would rather go with bash.

To activate it go to Preferences / Wicked Shell and select Cygwin - Bash as Default Static Shell. To make it work you need to provide your msysgit install directory under Shell root directory (do not provide the bin dir, but its parent).

You can change the Active Shell in the view menu of the Shell view.

You may see some strange characters on the prompt by now. These are control codes which cannot be displayed by the SWT widget (at least not the way wicked shell is implemented). So we need to change the prompt. Find your home directory (enter: cd ~;pwd) and create a .bashrc file with following content:
PS1='\w $ '
You should not exchange the $ sign at the end as it is used as command delimiter token by wicked shell. If you go with some other character, wicked shell will automatically insert a $ when you type your first character.

Considerations

WickedShell seems to have its limitations. So it is not possible to send a CTRL-C in a shell. I also experienced problems when trying to enter text a command prompts for (therefore the git flow init command does not work). Fortunately you need only single line commands once git flow is initialized correctly. If you need a full shell you could still Open an external Shell from the Shell view menu.

I tested WickedShell on Windows 7, according to the bugtracker there seems to be no support for Windows 8 yet.