Dropbox Client for ImageJ

Google Summer of Code 2014 - International Neuroinformatics Coordinating Facility (INCF, Belgian Node)

View project onGitHub

Dropbox Client for ImageJ

This project is sponsored by Google Inc. as a part of Google Summer of Code 2014 program.

Details of this project are:

  1. Organization: International Neuroinformatics Coordinating Facility (INCF)
  2. Mentor: Dimiter Prodanov, INCF Belgian Node
  3. Student Developer: Atin Mathur (mathuratin007@gmail.com)
  4. Working Period: May - August, 2014
  5. Public Repository: http://github.com/INCF/dbclient

ImageJ

ImageJ is a public domain Java image processing program inspired by NIH Image for the Macintosh. It runs, either as an online applet or as a downloadable application, on any computer with a Java 1.4 or later virtual machine. Downloadable distributions are available for Windows, Mac OS, Mac OS X and Linux.

It can display, edit, analyze, process, save and print 8-bit, 16-bit and 32-bit images. It can read many image formats including TIFF, GIF, JPEG, BMP, DICOM, FITS and "raw". It supports "stacks", a series of images that share a single window. It is multithreaded, so time-consuming operations such as image file reading can be performed in parallel with other operations.

ImageJ was designed with an open architecture that provides extensibility via Java plugins. Custom acquisition, analysis and processing plugins can be developed using ImageJ's built in editor and Java compiler. User-written plugins make it possible to solve almost any image processing or analysis problem. It is now being used in vast number of imaging applications ranging from skin analysis to neuroscience.

Project Proposal

Our project aims at developing an ImageJ plugin which will provide users with a Graphical user interface (GUI) to upload and download datasets (images, docs, folders, codes etc.) from their Dropbox accounts (using Dropbox Core APIs for Java) and open it directly into ImageJ. My project Proposal can be found here.

Project Milestones

In this section, I will describe how I developed this project.

Step 1 : Read Dropbox Core APIs for Java and ImageJ Basics. (Week 1)

The Dropbox Core APIs provides a flexible way to read and write to Dropbox. It includes support for advanced functionality like search, revisions, and restoring files. The Core API is based on HTTP and OAuth and provides low-level calls to access and manipulate a user's Dropbox account.

To make things as easy as possible, dropbox has provided several platform SDKs which can be imported into any development environment. The SDKs contain platform-specific libraries that wrap the raw HTTP calls to the Dropbox API.

I read the complete documentation of Dropbox Core APIs from here. It is quite simple and informative.

Step 2 : Wrote a sample program to use Dropbox API to access/upload/download from my Dropbox account. (Week 2)

Steps

  1. First, I registered a new Dropbox API app in the App Console (We'll need the app key to access the Core API).

  2. After that, I installed the Java SDK.

  3. Next, I installed Eclipse for Java for plugin development.

  4. Then, I created a test project and added the following code.

// Include the Dropbox SDK.
import com.dropbox.core.* ; 
import java.io.*;
import java.util.Locale;

public class testDropbox {
    public static void main(String[] args) throws IOException, DbxException {

        //  Get your APP Key and Secret from the Dropbox developers website.
        final String APP_KEY = "Insert <APP_KEY> here";
        final String APP_SECRET = "Insert <APP_SECRET> here";

        DbxAppInfo appInfo = new DbxAppInfo(APP_KEY, APP_SECRET);

        DbxRequestConfig config = new DbxRequestConfig("JavaTutorial/1.0", Locale.getDefault().toString());
        DbxWebAuthNoRedirect webAuth = new DbxWebAuthNoRedirect(config, appInfo);

        //  Have the user sign in and authorize your APP .
        String authorizeUrl = webAuth.start();
        System.out.println("1. Go to: " + authorizeUrl);
        System.out.println("2. Click \"Allow\" (you might have to log in first)");
        System.out.println("3. Copy the authorization code.");
        String code = new BufferedReader(new InputStreamReader(System.in)).readLine().trim();

        //  This will fail if the user enters an invalid authorization code.
        DbxAuthFinish authFinish = webAuth.finish(code);
        String accessToken = authFinish.accessToken;

        DbxClient client = new DbxClient(config, accessToken);

        //  Message will be displayed after successful connection with <Dropbox_Username>
        System.out.println("Linked account: " + client.getAccountInfo().displayName);

        //  Upload a file from your local machine to dropbox
        File inputFile = new File("Insert a <FILE_ABSOLUTE PATH> here>");
        FileInputStream inputStream = new FileInputStream(inputFile);
        try {
               DbxEntry.File uploadedFile = client.uploadFile("Insert <DROPBOX_FILE_PATH> where you want to upload the file here",
               DbxWriteMode.add(), inputFile.length(), inputStream);
            System.out.println("Uploaded: " + uploadedFile.toString());
        } finally {
            inputStream.close();
        }

        //  To list the children (files, sub-folders present in a given folder of Dropbox)
        DbxEntry.WithChildren listing = client.getMetadataWithChildren("Insert <DROPBOX_PATH> here");
        System.out.println("Files in the <DROPBOX_PATH> are :");
        for (DbxEntry child : listing.children) {
            System.out.println("    " + child.name + ": " + child.toString());
        }

        //  Download a file from <DROPBOX_FILE_PATH>
        FileOutputStream outputStream = new FileOutputStream("Insert <LOCAL_PATH> where you want to save the file here");
        try {
            DbxEntry.File downloadedFile = client.getFile("Insert <DROPBOX_FILE_PATH> here", null,
                outputStream);
            System.out.println("Metadata: " + downloadedFile.toString());
        } finally {
            outputStream.close();
        }
    }
}

After creating this test project, my next goal was to configure ImageJ with eclipse.

Step 3 : Configured ImageJ with Eclipse and created a plugin that could connect to a user's Dropbox account. (Week 3)

  1. Step by Step configured the ImageJ with Eclipse using the Method 1 of this tutorial.

  2. Created a sample plugin that could connect to a user's dropbox account.

Note: This plugin was running quite well in Eclipse on my computer. But, when I sent the JAR file to my mentor. He was not able to run it. After a lot of analysis, I found that I have not zipped the Dropbox SDK within the JAR file. After, I corrected this problem, my mentor was able to run this plugin on his computer. The plugin could be deployed now. The next step was to add the upload/download functionalities into the plugin.

Step 4 : Added the download/upload functionality in the ImageJ plugin. (Week 4)

As I had already created the test code for download/upload. I created separate functions for download/upload-ing a file in class DbxUtils.DbxUtility. These functions are as follows:

     /* Function to download a "File" from Dropbox given the complete path of the file in 
        Dropbox and also local machine path where the "File" has to be saved.
     */
    public void DbxDownloadFile(String FileDbxPath, String TargetLocalPath) throws IOException, DbxException
    {
        String fileName = FileDbxPath.substring(FileDbxPath.lastIndexOf("/"));
        TargetLocalPath += fileName;
        File SaveAsFile = new File(TargetLocalPath);
        OutputStream outputStream = new FileOutputStream(SaveAsFile);
        try {
        @SuppressWarnings("unused")
        DbxEntry.File downloadedFile = client.getFile(FileDbxPath, null, outputStream);
        } finally {
            outputStream.close();
        }
    }



     /* Function to upload a "File" to Dropbox given the complete path of the file in local machine and the 
      * Dropbox folder's path where "File" has to be saved.   
      */
    public void DbxUploadFile(String FileLocalPath, String TargetDbxPath) throws IOException, DbxException 
    {
        File inputFile = new File(FileLocalPath);
        InputStream inputStream = new FileInputStream(inputFile);
        try {
            if(!inputFile.isHidden()) {
                @SuppressWarnings("unused")
                DbxEntry.File uploadedFile = client.uploadFile(TargetDbxPath, DbxWriteMode.add(), inputFile.length(), inputStream);
            }
            } finally {
                inputStream.close();
            }
    }

Added a simple GUI for testing these functions. The user had to manually enter source address and target address in the text fields. In the next few weeks, I added the browse feature in the plugin.

Step 5 : Improved the Graphical User Interface of the plugin. (Week 5, 6)

In these week, I worked on improving the GUI for plugin. I set the JFrame to a proper size. I added instructions text for new user's help. I improved the layout of the plugin. I also added the menu options for Download and Upload. Also, added the download/upload folder option.

  1. Folders are downloaded by iteratively calling DbxDownloadFile() function for each file in the folder.

  2. Folders are uploaded by iteratively calling DbxUploadFile() function for each file in the folder.

Midterm Evaluation

Before Midterm evaluation, I was able to create a plugin with a simple GUI which could download/upload a file/folder from a user's dropbox account. After midterm, I worked to enable the following feature:

Browsing the Dropbox site remotely to add the source/target path for download/upload.

Step 6 : Added JTree for browsing Dropbox folders remotely. (Week 8, 9)

Adding the browsing feature in this plugin was quite a challenge. I had to download the metadata from dropbox site and then display it to user (using a JTree).

I tried following methods to do the same:

  1. After connecting to the user's Dropbox account, I called getMetaDataWithChildren(PATHNAME) recursively for complete dropbox account and added each folder/file to a node in JTree (Similar to a Directory Structure).

    But this took a lot of time. This was not feasible for our application. So, I solved this problem as follows:

  2. So, I downloaded metadata of only the top-level folders in the Dropbox Hierarchy and added those as JTree nodes. I only downloaded metadata of the folders which users wanted to Expand. This gave us with a run-time solution.

Thus, JTree was added for browsing functionality.

Step 7 : Changed the GUI quite a bit and added the open the file after upload/download functionality. (Week 10)

  1. In this week, I changed the GUI quite a bit. I used nested JPanels for proper alignment of the individual components(JButton, JTextField etc.) of the GUI. I also removed the menu bar and placed the Download/Upload panel in the right side of the original JFrame. Thus, the complete complete plugin was now has only one JFrame with many JPanels. The GUI looked quite elegant and simple to use.

  2. On recommendation of my mentor, I also added the open a file feature. So, I added the code to open the file/folder in OS default application.

Step 8 : Testing the code and handling exceptions wherever needed. (Week 11)

In these weeks, I mainly read the complete code and optimized some parts of the code. I also added exception handling wherever needed. I added various messages for user's help when an error occured. I also ensured that user does not enter wrong inputs in the plugin .

For each exception, There will be a message for the user.

Step 9 : Opening the file in ImageJ (Week 12)

Previously, I had added the code for opening the file in OS default application. On my mentor's recommendation, I changed it to ImageJ.

If a file is not supported by the ImageJ, it will show an error message.

Step 10 : Created a new thread for downloading/uploading. (Week 12)

Swing GUI has only a single thread. Thus, during a long process such as download/upload the plugin became unresponsive. On my mentor's advice, I spawned a new thread for download/upload.

Thread thread = new Thread("New Thread") {
                              public void run(){
                                    // Call to Upload/Download functions
                            };
                        thread.start();

After creating a new thread for download/upload, plugin became responsive.

This plugin now supports the multithreading, i.e. Multiple upload/download tasks can be performed simultaneously.

Installation

  1. Install ImageJ.
  2. Download DropboxClient plugin from here.
  3. Put this JAR file (MyCloudJ_.jar) inside the plugins folder of your ImageJ.
  4. It's ready to use. Start your ImageJ.
  5. In the plugins menu, open CloudConnect -> MyCloudJ.
  6. Connect MyCloudJ plugin to your dropbox account and use it to download/upload.

Screen shots of MyCloudJ plugin

This repository is maintained by Atin Mathur (mathuratin007@gmail.com) and Dimiter Prodanov (dimiterpp@gmail.com). Please feel free to contact us for any help regarding the plugin installation or source code pitfalls.