Bouncycastle, OSGi and uber-jars

I have recently tried to use SSHj in an OSGi bundle.
I had decided to wrap this library and its dependencies in my own bundle (something equivalent to an uber-jar but compliant with OSGi). In a classic context, you would use the Maven Shade plug-in. In an OSGi context, you can simply use the Maven Bundle plug-in, with the embedded-dependency directive.

Anyway, the difficulty here came because SSHj uses Bouncycastle as a security provider. And you cannot do what you want with it. The first attempt to build my all-in-one bundle resulted in a signature error during the Maven build.

Invalid signature file digest for Manifest main attributes

Indeed, some files in the JAR were signed, and some others were not. I solved it by using the Maven ANT plugin, removing signature files from my jar and repackaging it. The JAR could finally be built. Unfortunately, later at runtime, another error came out.

JCE cannot authenticate the provider BC

Looking at the logs of SSHj, no provider was found while the right classes were in the bundle’s class path. And everything was working outside of OSGi. So, there was no error with compilation levels or in my code. For some reason, Bouncycastle was not loaded by the JVM.

The explanation is that JCE (Java Cryptography Extension) providers are loaded in a special way by the JVM. First, they must be signed. And it seems not any certificate can be used (it must be approved by the JVM vendors). Bouncycastle is signed. But if you wrap it into another JAR, you will lose the signature. And then, these providers must be loaded at startup. If you apply this to an OSGi context, it means you cannot deploy Bouncycastle as a bundle whenever you want.

Finally, I solved my issue by…

  • … following Karaf’s documentation and making Bouncycastle a part of my Karaf distribution (copy it in lib/ext and updating some configuration files). See this POM to automate such a thing for you custom Karaf distribution.
  • … not importing org.bouncycastle.* in my packages. That’s because putting these libraries under lib/ext means these packages are considered as root classes (just like java.*). No need to import them then.
  • … making sure all the bundles that depend on Bouncycastle would use the same version. I achieved it by updating the dependencyManagement section in my parent POM.

And although it was not a real issue, I decided to put SSHj as Karaf feature. This way, no need to make an uber-jar. And I can reuse it as much as I want. See this file for a description of this feature (ssh4j-for-roboconf). The dependencies that are not already OSGi bundles are deployed through the wrap protocol.

I spent about a day to solve these issues. That’s why I thought an article might help.

Probes conflicts with PAX-Exam 4

Edit: this bug is even worst than what I was thinking.
The explaination below is incomplete. The bug occurred randomly. And probe names were a wrong lead. In fact, I have a race condition when loading bundles. I limited its impact by defining an Import-Package header in my probe.

// Force the use of the AgentMessagingInterface from the agent's bundle.
probe.setHeader( "Import-Package", "net.roboconf.agent" );

It imports the package that was conflicting. This way, I am sure my probe does not inject its own version of the package. But still, this is a very curious bug.

Here is the original post…

I have just lived an unexpected thing while adding a new integration test on Roboconf. “Just lived” is a little bit exagerated. I spent two hours to understand and try to solve it. Anyway, these tests are run with PAX-Exam 4. They allow us to verify our OSGi bundles work as expected in an OSGi environment.

So, here is what happened.
I have one test called AgentInitializationTest. And I added DelayedAgentInitializationTest. Run individually in Eclipse, they both worked. Run in a single row with Maven, the last one was failing.

I had an @Inject annotation which was failing.
There was an IllegalArgumentException thrown by Field.java:741. I did not understand why I had this. I tested a workaround by injecting the bundle context and retrieving my service reference by hand. But I still had errors when casting my service to its interface.

ServiceReference serviceReference = context.getServiceReference( AgentInterface.class );
AgentInterface itf = (AgentInterface) context.getService( serviceReference );

That was not working, while context.getService() returned an instance of AgentInterface. As reminded on Oracle’s web site, there are two reasons for such a thing to happen. Either both objects have no relation (which is not the case here, one implements the other), or theirs definitions come from different class loaders.

And indeed: my service and its interface came from different class loaders. The Agent class was exported by its bundle whereas the AgentInterface interface was exported by a PAX-Exam probe. This was not normal at all, since my new test was not adding agent classes in its probe. Said differently, both tests had different probes configurations. Besides, my tests were all annotated with the PerMethod reactor strategy to isolate my tests.

Eventually, I @ignored AgentInitializationTest and run my tests with Maven. Miracle, DelayedAgentInitializationTest was now working. I eventually renamed my new test in AgentWithDelayedInitializationTest and now, everything is working fine.

Clearly, the probe from a previous test was injected into my new test. And this is most likely related to the test names. My intuition is that if they are wrongly chosen, there can be conflicts in probes injection. So, if you encounter the same issue one day, just make sure all your tests have different names and that no one prefixes another test name.

Detecting New Images in the Clipboard

I have 2 applications that should work together.
These applications are end-user applications. The link between them is in fact the system’s clipboard (this is a Windows 7’s use case, but it might be encountered on other platforms). Basically, the idea is that the user selects an image in the first application and copies it in the clipboard. The second application should detect this new image and process it.

By browsing the web, I found out that there is no perfect solution. Sun did not provide a complete solution for this in Java because there were portability issues. One alternative was to use the ava.awt.datatransfer.ClipboardOwner class. A subclass instance acquires the ownership of the clipboard by putting something into it. When another application copies something into the clipboard, the previous owner is notified that it has lost the ownership. The implementation would be something like:

public class ClipboardWatcher implements ClipboardOwner {

	/**
	 * @see java.awt.datatransfer.ClipboardOwner
	 * #lostOwnership(java.awt.datatransfer.Clipboard, java.awt.datatransfer.Transferable)
	 */
	@Override
	public void lostOwnership( Clipboard clipboard, Transferable contents ) {
		// Get the clipboard's content and process it if required
		// ...

		// Acquire the ownership again by putting the same content
		// (Same content because otherwise, copy/paste does not work anymore for other applications)
		// ...
	}
}

This is not very convenient.
I also discovered it was not working very well when there are several running applications.
So, I implemented a solution with a thread which polls the clipboard regularly. I put here a complete sample with an example of image processing.

import java.awt.Image;
import java.awt.Toolkit;
import java.awt.datatransfer.Clipboard;
import java.awt.datatransfer.ClipboardOwner;
import java.awt.datatransfer.DataFlavor;
import java.awt.datatransfer.StringSelection;
import java.awt.datatransfer.Transferable;
import java.awt.datatransfer.UnsupportedFlavorException;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.concurrent.atomic.AtomicBoolean;

import javax.imageio.ImageIO;

/**
 * @author Vincent Zurczak
 */
public class ClipboardWatcher implements ClipboardOwner {
	
	static final File PREVIEW_DIRECTORY = new File( System.getProperty( "java.io.tmpdir" ), "Image-Store" );
	private final List<PreviewFileListener> listeners;
	private final SimpleDateFormat sdf;
	private final AtomicBoolean run;
	
	
	/**
	 * Constructor.
	 */
	public ClipboardWatcher() {
		 this.listeners = new ArrayList<PreviewFileListener> ();
		 this.sdf = new SimpleDateFormat( "yy-MM-dd---HH-mm-ss" );
		 this.run = new AtomicBoolean( true );
		 
		 if( ! PREVIEW_DIRECTORY.exists()
				 && ! PREVIEW_DIRECTORY.mkdir()) {
			 // TODO: process this case in a better way
			 System.out.println( "The preview directory could not be created." );
		 }
	}
	
	
	/**
	 * @param e
	 * @return
	 * @see java.util.List#add(java.lang.Object)
	 */
	public boolean addPreviewFileListener( PreviewFileListener e ) {
		synchronized( this.listeners ) {
			return this.listeners.add( e );
		}
	}


	/**
	 * @param o
	 * @return
	 * @see java.util.List#remove(java.lang.Object)
	 */
	public boolean removePreviewFileListener( PreviewFileListener o ) {
		synchronized( this.listeners ) {
			return this.listeners.remove( o );
		}
	}
	
	
	/**
	 * @see java.awt.datatransfer.ClipboardOwner
	 * #lostOwnership(java.awt.datatransfer.Clipboard, java.awt.datatransfer.Transferable)
	 */
	@Override
	public void lostOwnership( Clipboard clipboard, Transferable contents ) {
		// Acquire the ownership again is not working very well for a periodic check
	}
	
	
	/**
	 * Starts polling the clipboard.
	 */
	public void start() {
		
		// Define the polling delay
		final int pollDelay = 5000;
		
		// Create the runnable
		Runnable runnable = new Runnable() {
			@Override
			public void run() {
				
				Transferable contents = null;
				while( ClipboardWatcher.this.run.get()) {
					
					// Get the clipboard's content
					Clipboard clipboard = Toolkit.getDefaultToolkit().getSystemClipboard();
					try {
						contents = clipboard.getContents( null );
						if ( contents == null 
								|| ! contents.isDataFlavorSupported( DataFlavor.imageFlavor ))
							continue;
						
						Image img = (Image) clipboard.getData( DataFlavor.imageFlavor );
						saveClipboardImage( img );
						clipboard.setContents( new StringSelection( "The clipboard watcher was here!" ), ClipboardWatcher.this );
						
					} catch( UnsupportedFlavorException ex ) {
						ex.printStackTrace();

					} catch( IOException ex ) {
						ex.printStackTrace();
						
					} catch( Exception e1 ) {
						// We get here if we could not get the clipboard's content
						continue;
					
					} finally {						
					
						try {
							Thread.sleep( pollDelay );
							
						} catch( InterruptedException e ) {
							e.printStackTrace();
						}
					}
				}
			}
		};
		
		// Run it in a thread
		Thread thread = new Thread( runnable );
		thread.start();
	}
	
	
	/**
	 * Stops listening.
	 */
	public void stop() {
		this.run.set( false );
	}
	

	/**
	 * Saves the image residing on the clip board.
	 */
	public void saveClipboardImage( Image img ) {

		if( img != null ) {	    	
			if( img instanceof BufferedImage ) {
				try {
					File file = new File( PREVIEW_DIRECTORY, this.sdf.format( new Date()) + ".jpg" );
					ImageIO.write((BufferedImage) img, "jpg", file );
					for( PreviewFileListener listener : this.listeners )
						listener.newCreatedFile( file );

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

			} else {
				System.out.println( "Unsupported image: " + img.getClass());
			}	
		}
	}
	
	
	/**
	 * A interface to listen to image detection.
	 */
	public interface PreviewFileListener {

		/**
		 * Notifies this instance that an image file was created from the clip board.
		 * @param imageFile the image file that was saved on the disk
		 */
		void newCreatedFile( File imageFile );
	}
}

There is another alternative for Windows platforms.
However, it was only implemented for an OSGi context. Notice that it should not be too difficult to adapt it for standalone Java applications. I read and understood most of the code, but I was a little bit lazy on this one. Check this blog entry for more details. The code is hosted on GitHub.

SWT snippet: an image viewer with a scrolled composite

I don’t know why, but among all the SWT controls, ScrolledComposite is the most difficult for me. I mean, every time I have to use it, it does not work at the first try.

So, here is a snippet of my own. It completes the usual ones provided by SWT.
It consists in viewing an image in a dialog. If the image is bigger than a given size, then scroll bars appear.

package com.ebmwebsourcing.swt.snippets;

import org.eclipse.swt.SWT;
import org.eclipse.swt.custom.ScrolledComposite;
import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.graphics.ImageData;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.swt.widgets.Text;

public class NewSnippet {

	public static void main( String [] args ) {

		// Display + shell
		Display display = new Display ();
		Shell shell = new Shell( display );
		shell.setLayout( new GridLayout( 2, false ));

		// Load an image
		//ImageData imgData = new ImageData( "your image path" );
		ImageData imgData = new ImageData( "D:/Divers/wallpapers/1920-1200-74378.jpg" );
		Image image = new Image( display, imgData );

		// The scrolled composite
		ScrolledComposite sc = new ScrolledComposite( shell, SWT.H_SCROLL | SWT.V_SCROLL );
		GridData layoutData = new GridData( GridData.FILL_HORIZONTAL );
		layoutData.horizontalSpan = 2;
		layoutData.heightHint = 400;
		sc.setLayoutData( layoutData );

		Label imgLabel = new Label( sc, SWT.NONE );
		imgLabel.setImage( image );
		imgLabel.setSize( imgLabel.computeSize( SWT.DEFAULT, SWT.DEFAULT ));
		sc.setContent( imgLabel );

		Label l = new Label( shell, SWT.NONE );
		l.setText( "Nom actuel :" );
		layoutData = new GridData();
		layoutData.verticalIndent = 20;
		l.setLayoutData( layoutData );

		Text text = new Text( shell, SWT.SINGLE | SWT.BORDER | SWT.READ_ONLY );
		text.setText( "The image name" );
		layoutData = new GridData( GridData.FILL_HORIZONTAL );
		layoutData.verticalIndent = 20;
		text.setLayoutData( layoutData );

		// Run it
		shell.setSize( 800, 600 );
		shell.open();
		while( ! shell.isDisposed()) {
			if( ! display.readAndDispatch())
				display.sleep();
		}

		display.dispose();
		image.dispose();
	}
}

And here are two snapshots of the result.

An image viewer that scrolls when needed
An image viewer that scrolls when needed
An image viewer that scrolls when needed (scrolled view)
An image viewer that scrolls when needed (scrolled view)