Creating your own thread dispatcher in Java

Java offers ExecutorService, which can be used for managing and dispatching threads. But ExecutorService has limitations. One of them being the fact that if you create a fixed thread pool, you need to define all your threads up front. What if you’ll be spawning a million threads (but only a handful will be running at a given moment)? That would take up a lot of memory. And in some cases, you may need to know the outcome of certain threads to schedule new threads. It’s hard and cumbersome to do this using ExecutorService.

On the other hand, you can easily create your own thread dispatcher service, which can be limited to run only a certain number of threads at a time. See code snippet below:

final static int MAX_THREADS_AT_A_TIME = 10;
static int currentlyRunningThreadsCount = 0;
static Object dispatcherLock = new Object();

public static void main(String args[])
{
	for(int i=0; i<100; i++)
	{
		synchronized(dispatcherLock)
		{
			currentlyRunningThreadsCount++;
		}
		final int thisThreadCount = i+1;
		new Thread(new Runnable() {
			@Override
			public void run() {
				//Do something
				System.out.println("Thread "+thisThreadCount+" starting.");
				try { Thread.sleep(5000); } catch(InterruptedException e) { }
				System.out.println("Thread "+thisThreadCount+" finished.");
				synchronized(dispatcherLock)
				{
					currentlyRunningThreadsCount--;
					dispatcherLock.notify();
				}
			}
		}).start();
		if(currentlyRunningThreadsCount >= MAX_THREADS_AT_A_TIME)
		{
			synchronized(dispatcherLock)
			{
				try { dispatcherLock.wait(); } catch(InterruptedException e) { }
			}
		}
	}
}

Let’s break it down. In this case we’re spawning 100 threads, but limiting it to run only 10 threads at a time. Everything outside of the thread runnable is part of the “dispatcher” service. The dispatcher loop uses a counter currentlyRunningThreadsCount to track how many threads are running at a time. If there are 10, it wait()’s on a lock object. And as each thread finishes its work, it decrements currentlyRunningThreadsCount and calls notify() on the lock object, which wakes up the dispatcher and it moves on to spawn more.

Pretty simple, right?!

JasperReports nuances

JasperReports is an engine that can allow you to generate reports in HTML, PDF, or many other formats. When I say reports here, I mean reports that are essentially pieces of paper that convey something meant to be reviewed by someone. Invoices, Statements, Forecasts, or anything that needs to be dynamically presented on paper for review is a good candidate.

One positive about JasperReports is that it’s all Java based, and plugs in well into your Java ecosystem. On the other hand, if you’re just now looking for a reporting engine, I may try to dissuade you from using JasperReports. It seems to have become outdated. There have been few updates in the last couple years (if any), and community support seems to be waning (you only find old blog posts, forums threads, etc). And documentation is also a bit lacking. But if you’re already using JasperReports, this post is for you.

It took me a while to figure how to get JasperReports to do certain things, because again the documentation is weak. So I figured I’d share these insights just in case someone else is struggling with the same thing.

  • Setting the foreground/background color of a field programatically
    This was not obvious at all. Sometimes you need to set the background color or text color of a field dynamically, based on some data value. Let’s say a data field literally has the color in it: for example $F{BACKCOLOR}, and you want to set the background color of a field to the value contained in $F{BACKCOLOR} (say “#0000FF”). In order to accomplish this, you’ll need to edit the properties of the TextField, and set this property:

    net.sf.jasperreports.style.backcolor

    …to this value:

    $F{BACKCOLOR}

And similarly to set the foreground color (text color), set this property for the TextField:

net.sf.jasperreports.style.forecolor

…to a field of your choosing that has the color in it. (Or hard code the color by typing into the value for this property, surrounded by double quotes to specify a constant).

  • JSON queries against your DataSet
    JasperReports seems to play pretty well with JSON. But something that isn’t obvious is a way to query/filter the JSON data within the JasperReports engine, which it is populated into a data set.To demonstrate how to do this, let’s take an example JSON data:

[{“letters”:[{“category”:”A to C”,”data”:[“a”,”b”,”c”]},{“category”:”D to F”,”data”:[“d”,”e”,”f”]}]}]

Or more visually friendly, like so:

JasperReportsJsonExample

Now let’s say you want to limit your JasperReports Data Set to “letters”, and furthermore a certain category. What you need to do is edit the query for the Data Set, and specify the following:

letters(category==A to C)

And that’s it!

  • More to come later

AmazonS3Client to loop through batches of S3 files objects

AWS provides the AmazonS3Client class, which is part of the AWS Java SDK. This class can be used to interact with files in S3.

An important feature to note of the AmazonS3Client is that it limits results to batches of 1000. If you have less than 1000 files, then all is good. You can use amazonS3Client.listObjects(bucketName); and it will provide all the objects in a bucket.

But if the bucket contains more than 1000 files, you will need to loop through the files in batches. This is not entirely obvious and can cause you to miss files (as I certainly did)!

To get started, you would initiate AmazonS3Client like so:

AmazonS3Client amazonS3Client = new AmazonS3Client(new BasicAWSCredentials(KEY, SECRET));

The approach I like to take is to first loop through and collect all the files up front like so:

ObjectListing objectListing = amazonS3Client.listObjects(bucketName);
List<S3ObjectSummary> s3ObjectSummaries = objectListing.getObjectSummaries();
while (objectListing.isTruncated()) 
{
   objectListing = amazonS3Client.listNextBatchOfObjects (objectListing);
   s3ObjectSummaries.addAll (objectListing.getObjectSummaries());
}

Note: if memory is a concern or you have an unlimited number of files, you can simply modify the approach to do whatever you need to with each file as you fetch it in batches from the API, instead of collecting them up front.

If you first collected them in a List up front, you can then loop through each file like so:

for(S3ObjectSummary s3ObjectSummary : s3ObjectSummaries)
{
	String s3ObjectKey = s3ObjectSummary.getKey();
	//Do whatever with s3ObjectSummary

 

Sorting a JSON Array in Java

There are a number of approaches you can take. But a simple one is to first convert it to a Java Collection, and use Collections.sort() with a custom comparator.

The example I’ll follow consists of an org.json.JSONArray which is has (and only has) org.json.JSONObject’s. So a json array of json objects, which is pretty common. Say you want to sort the JSONObjects in the JSONArray, based on a key in the JSONObject.

Let’s start by converting a JSONArray to a Collection of JSONObjects, using the java List type:

List<JSONObject> myJsonArrayAsList = new ArrayList<JSONObject>();
for (int i = 0; i < myJsonArray.length(); i++)
    myJsonArrayAsList.add(myJsonArray.getJSONObject(i));

Now you can use Collections.sort() with a custom comparator. Let’s say you have a key named “key” in each json object, which maps to an int, and you want to sort on int value. You would use the following code:

Collections.sort(myJsonArrayAsList, new Comparator<JSONObject>() {
    @Override
    public int compare(JSONObject jsonObjectA, JSONObject jsonObjectB) {
    	int compare = 0;
    	try
    	{
    		int keyA = jsonObjectA.getInt("key");
    		int keyB = jsonObjectB.getInt("key");
    		compare = Integer.compare(keyA, keyB);
    	}
    	catch(JSONException e)
    	{
    		e.printStackTrace();
    	}
    	return compare;
    }
});

That’ll do. Now, let’s take it a step further. Let’s say the values in the key field of each json object are Strings, and you want to sort based on the Strings, but in a particular order. You want the string “oranges” to come first, “bananas” to come second, “pineapples” to come third, and “apples” to come last.

An easy way to go about this is to create a HashMap, and assign these strings integer values. Then use those integer values to compare. Here’s what the code would look like for that:

Collections.sort(myJsonArrayAsList, new Comparator<JSONObject>() {
    @Override
    public int compare(JSONObject jsonObjectA, JSONObject jsonObjectB) {
    	int compare = 0;
    	try
    	{
			HashMap<String,Integer> fruitTypeSorts = new HashMap<String,Integer>();
			fruitTypeSorts.put("orange", 1);
			fruitTypeSorts.put("bananas", 2);
			fruitTypeSorts.put("pineapples", 3);
			fruitTypeSorts.put("apples", 4);
			int valueA=fruitTypeSorts.get(jsonObjectA.getString("key"));
			int valueB=fruitTypeSorts.get(jsonObjectB.getString("key"));
			return Integer.compare(valueA, valueB);
    	}
    	catch(JSONException e)
    	{
    		e.printStackTrace();
    	}
    	return compare;
    }
});

And voila! Now you have the List sorted based on the key value, with objects with key values oranges being first, then bananas, and so on.

To tie it all together, you want to convert it back to a JSONArray. To put the sorted JSONObjects back into your original array, simply:

myJsonArray = new JSONArray();
for (int i = 0; i < myJsonArrayAsList.size(); i++) {
	myJsonArray.put(myJsonArrayAsList.get(i));
}

Recovering Java keystore password

So I hadn’t updated my Android app in 5 years. I had worked on an update for the past couple months, and was finally ready to upload it. I knew I had my Java keystore backed up, and the password written down. But come time to deploy the updated .apk, I couldn’t get into the keystore! The password I had written down 5 years ago was not it.

I was very frustrated, and disheartened after all the recent effort. Should I delete my old application, and upload the update as a completely new one? But then I’d lose all my current users.

I knew there are only a handful of passwords that I use, sometimes with slight variations, and sometimes multiple of my standard passwords put together. So I thought that I’d try to crack it.

Enter the Java based Android Keystore Password Recovery tool: http://maxcamillo.github.io/android-keystore-password-recover/. It’s a pretty configurable brute forcer. And it worked for me. Though it took several tries to get it just right so it could crack it, and I’ll explore how to best use it in this post.

The best way to use Android Keystore Password Recovery tool is with the Smart Dictionary mode, which tries variations of the words in a dictionary. It appends the words in the dictionary together in all orders, and adds numbers to it. It can also try upper case/lower case for the first letter of each word when trying variations. This is useful when you know roughly what your password was, but you don’t remember the case, or the numbers you added around it. And it’s especially useful if you concatenate two or more of your passwords together, but don’t remember in what order or what variation of your password (as was my case).

To run it, download the .jar (in my case I imported the source into Eclipse and ran it that way). Make sure you have java installed, and you can run it from the command line.

First you’ll want to create a dictionary of passwords that you usually use. Try to think of all variations and break them up into as little of pieces as possible. For example, if your password is CatManDu57, but you don’t remember if you variated it to 57DuManCat or something else, then break the dictionary into the following words:

Cat

Man

Du

57

Try to think of all the variations of cases that you use. A limitation in Android Keystore Password Recovery is that it is only capable of trying the first letter of the word with uppercase and lower case. So you may want to put in different combinations of your word, i.e. cAt, CaT, cAT (all added to the dictionary) should do it. And Android Keystore Password Recovery will try all the variations.

To run it, use the following command:

java -jar AndroidKeystoreBrute_v1.06.jar -m 3 -k “/path/to/mykeystore” -d “/path/to/wordlist.txt”

And voila! It will try all combinations in your wordlist, individually, or concatenated together.

Note that there’s a very useful and configurable feature called “pieces”, which isn’t very obvious from the documentation. You can define the number of pieces from the wordlist that can be concatenated together to form your password. This can narrow down the search for the password a lot so that the tool can go only up to a certain number of combinations. For example if we go back to the CatManDu57 example, that’s 4 total pieces. And since you know that there are at least 4 pieces together, and a maximum of 4 pieces, you’d use between “4 and 4” and it will try all combinations that way. I believe the default number of pieces it goes to is 1 to 64, and that will take much longer to run, so you’re better off limiting this if you can remember the number of pieces. Note: that a piece can be repeated within the same password combination it tries, so for our example with 4 pieces, one of the combinations will be CatCatCatCat, and one will be 57575757, and every combination in between, in every order. So you can see this can be very useful if you have some idea what your password was, especially if you remember pieces of it.

Some other notable parameters are as follow: -l sets the minimum password length. So if you knew it’s at least a certain number of characters, you should use it to narrow it down. -firstchars sets the first few characters of your password, and should be used if you remember for sure what the first few characters of the password were. -p uses common replacements such as “@” or “a”. And -onlylower makes it not mess with the case of the first letter of each word in your dictionary… very useful if you know exactly the case and that’s what you should use in the dictionary for each piece (which was my case).

The better and more precise the parameters you provide based on what password you use, obviously, the more likelihood of result. Try different combinations and try to narrow it down. I had to go through lots of iterations of wordlists and parameters (especially pieces) to get it just right.

Another mode that I found useful was the brute forcing method, but limited to a certain set of characters. This one will require editing the source code. If you knew you used some variation of CatManDu57, and want to simply brute force with just those characters, you can edit BrutePasswd.java in the source, and limit it to the characters:

‘C’,’c’,’A’,’a’,’T’,’t’,’M’,’m’,’N’,’n’,’D’,’d’,’U’,’u’,’5′,’7′.

And this will do a brute of all combinations of all permutations starting from whatever length you specify, and will most certainly crack your password eventually if you’re sure those were the only characters in your password. Of course this becomes a lot slower, and may take weeks or months, the more the characters you have and the longer the length of the password.

Another notable mention for a cracker is John The Ripper (http://www.openwall.com/john/). Most importantly, it has support for Java keystores. Download the community enhanced version, and use the “keystore2john” utility to convert it to John the Rippper (JtR) format. I tried this but unfortunately didn’t quite figure out how to configure it with my own dictionary. Nor could I get it to repeat pieces of the dictionary which I knew was important in my password. I did try it on a bogus keystore I created with a simple password (“test57”), just to test, and it did crack that very quickly, since it does have a built in dictionary.

Performance note: while Android Keystore Password Recovery works pretty fast (I was getting 200,000 combinations of passwords per second), John The Ripper beat that in several orders of magnitude. JtR was guessing over a million combinations of passwords per second! Holy batman.

Last notable mention is Hashcat (https://hashcat.net/hashcat/), which is supposed to be a very fast password cracker, and can also use GPU to do the hashing which will greatly speed it up even more than JtR. With this, you can set up a cluster of GPU-enabled EC2 machines in AWS (or anywhere else), and run it and it will eventually crack. Unfortunately, at the time of writing this post, hashcat does not support Java keystores, though I did see in a few places (such as a poll on what feature to add) that it’s a commonly requested feature in Hashcat. Maybe if I have time, I’ll contribute to that. But for now I’m happy because I have my lost keystore password recovered!

Good luck!

P.S. check out my Android app at: https://play.google.com/store/apps/details?id=com.synappze.stoptimer and https://github.com/AdeelMufti/StopTimer

Throttling against Egnyte RESTful API from Java

Last week I wrote about how to interface with the Egnyte RESTful API using a custom Java class, so that you can use methods like uploading, downloading, or listing files from anywhere in your code (https://adeelscorner.wordpress.com/2016/07/24/creating-java-helper-class-to-interact-with-egnyte-restful-api/).

It turns out that Egnyte throttles requests to their API. According to their documentation, it is throttled at two transactions per second, so one per 500ms. And if your network connection is fast enough, you will most certainly hit that limit and get denial of service from the API, if you make several calls to API back to back. So, you need to throttle the requests. I’ll talk on how to go about doing that in this post.

Recall the EgnyteHelper class that I created in the aforementioned blog post. A few modifications to this class and methods, with the use of a Timer, can easily overcome this issue. And I’ll show you how you can retry your call recursively if it still gets blocked.

First let’s define some class level constants, variables, a constructor, and two methods to assist. And then I’ll show you how you can use that in one of the Egnyte API helper methods (such as listFileOrFolder()) to prevent against throttling, and assist in retrying.

 private static final int EGNYTE_API_WAIT_MS_BETWEEN_TRANSACTIONS = 501;
 private static final int RETRY_API_CALL_TIMES = 3;

 private Timer timer;
 private Object lock;
 private HashMap < String, Integer > recurseHelperHashMap;

 public EgnyteHelper() {
  lock = new Object();
  recurseHelperHashMap = new HashMap < String, Integer > ();
 }

 private void setTimer() {
  timer = new Timer();
  timer.schedule(new TimerTask() {
   @Override
   public void run() {
    timer.cancel();
    timer = null;
    synchronized(lock) {
     lock.notify();
    }
   }
  }, EGNYTE_API_WAIT_MS_BETWEEN_TRANSACTIONS);
 }

 private void waitForTimerOrSetTimer() {
  if (timer == null)
   setTimer();
  else {
   synchronized(lock) {
    try {
     lock.wait();
    } catch (InterruptedException e) {}
    setTimer();
   }
  }
 }

Starting from the top, we define a static variable called EGNYTE_API_WAIT_MS_BETWEEN_TRANSACTIONS. You’ll notice that I’ve set it to 501ms instead of the 500ms limit, so there’s no chance a boundary case is hit and you get blocked. The RETRY_API_CALL_TIMES static variable define show many times you want your method to retry before giving up. The timer variable is used to do the actual waiting, lock variable is used when a method needs to wait for the previous call’s 500ms to elapse, and recurseHelperHashMap is used to assist in recursion (more on that later).

In the constructor we simply just initialize the lock and recurseHelperHashMap. The next two methods are where the magic happens. setTimer() initializes a Timer to wait 501ms, and then calls notify on the lock object, which we catch later. So if a subsequent method is waiting for the lock, it can move on, and do the Egnyte API RESTful call. waitForTimerOrSetTimer() is the method that is actually used in our helper methods. It checks if the timer has already been set, and if not, it simply creates it and moves on. But if the timer is set, it waits on the lock object, until the timer from the previous call releases it, and then it moves on, thus preventing your helper method from moving too fast and hitting the throttle limit.

Next let’s see how we can put this all together and use it in a helper method such as listFileOrFolder(). Here is a modified version of listFileOrFolder() that we explored last time:

 public JSONObject listFileOrFolder(String fullFileOrFolderPathWithSpaces) throws Exception {
  String thisMethodName = Thread.currentThread().getStackTrace()[1].getMethodName();

  waitForTimerOrSetTimer();

  JSONObject listing;

  URL url = UriBuilder.fromPath(Constants.EGNYTEAPI_BASE_URL + "fs/" + fullFileOrFolderPathWithSpaces).build().toURL();
  URLConnection urlConnection = url.openConnection();
  HttpURLConnection httpUrlConnection = (HttpURLConnection) urlConnection;
  httpUrlConnection.setRequestMethod("GET");
  httpUrlConnection.setRequestProperty("Authorization", "Bearer " + Constants.EGNYTEAPI_AUTH_TOKEN);

  httpUrlConnection.connect();

  if (httpUrlConnection.getResponseCode() != HttpURLConnection.HTTP_OK && httpUrlConnection.getResponseCode() != HttpURLConnection.HTTP_CREATED) {
   Map < String, List < String >> hdrs = httpUrlConnection.getHeaderFields();
   if (hdrs.containsKey("Retry-After")) {
    if (!recurseHelperHashMap.containsKey(thisMethodName))
     recurseHelperHashMap.put(thisMethodName, 1);
    else
     recurseHelperHashMap.put(thisMethodName, recurseHelperHashMap.get(thisMethodName) + 1);

    if (recurseHelperHashMap.get(thisMethodName).intValue() <= RETRY_API_CALL_TIMES) {
     Thread.sleep(Integer.parseInt(hdrs.get("Retry-After").get(0)) * 1000);
     return listFileOrFolder(fullFileOrFolderPathWithSpaces);
    }
   }

   listing = new JSONObject();
  } else {
   Scanner scanner = new Scanner(httpUrlConnection.getInputStream());
   scanner.useDelimiter("\\A");
   String returnJsonString = (scanner.hasNext() ? scanner.next() : "{}");
   scanner.close();
   listing = new JSONObject(returnJsonString);
  }

  httpUrlConnection.disconnect();

  if (recurseHelperHashMap.containsKey(thisMethodName))
   recurseHelperHashMap.remove(thisMethodName);

  return listing;
 }

Starting from the top, we enter the method, get the method name (I used reflection instead of hardcoding the method name in a string, since I can copy/paste that code in any other helper method), and call waitForOrSetTimer(). This makes the current method call either wait first if a previous call was made within the last 500ms, or it sets the timer if no previous call’s throttle limit was waiting. Next you see that we make the RESTful API call to Egnyte, and then we detect if there was a non-normal HTTP code returned. Fortunately Egnyte sends back an HTTP header in the response if your request does get throttled, called “Retry-After”, which defines the number of seconds we need to wait before retrying. If the header is detected, the code starts with the recursive magic. First it sleeps for the amount of time Egnyte API wants us to sleep (you may want to put EgnyteHelper’s helper method calls on a background thread so your main thread doesn’t get blocked). Then it recursively calls the helper method, up to “RETRY_API_CALL_TIMES” times, before giving up. It does so with the help of the recurseHelperHashMap class level variable, which maps the method name to the number of times the recursion has already happened. Finally when exiting the method, we clean up the entry for this method from recurseHelperHashMap.

And that’s all!

Note that you should have one instance of EgnyteHelper shared per your whole Java application. So if different parts of the application need to access the Egnyte API at the same time, EgnyteHelper can throttle all those requests appropriately using the same timer, application-wide.

Creating Java “Helper” class to interact with Egnyte RESTful API

Egnyte is a HIPAA compliant secure cloud file service. If you’re using Egnyte, and have Java applications that need to access your files on Egnyte, you can create a “Helper” class to interact with the RESTful API Egnyte provides. You could create methods such as “downloadFile()”, “uploadFile()”, “listFileOrFolder()” to use anywhere in your Java code.

As of when this blog post was written, Egnyte does not provide a native Java API. But it does provide a pretty rich RESTful API using web services. The documentation for this API is here: https://developers.egnyte.com/docs

The API requires an authentication token sent in with the HTTP headers. So first you’ll need to obtain this token using your Egnyte account username and password. More details on obtaining this token is in the documentation referenced above.

Once you have your authorization token, you can get started with making RESTful calls.

Let’s define two variables in a Constants class, called EGNYTEAPI_BASE_URL and EGNYTEAPI_AUTH_TOKEN. The base URL should be “https://(your-domain).egnyte.com/pubapi/v1/”, and the auth token should be the simple alphanumeric string you obtained from Egnyte. You’ll need to use these in all the methods that interact with the Egnyte RESTful API.

Here’s a snippet of what a downloadFile() method would look like:

public File downloadFile(String fullPathWithSpaces) throws Exception {
 File file = null;

 URL url = UriBuilder.fromPath(Constants.EGNYTEAPI_BASE_URL + "fs-content/" + fullPathWithSpaces).build().toURL();
 URLConnection urlConnection = url.openConnection();
 HttpURLConnection httpUrlConnection = (HttpURLConnection) urlConnection;
 httpUrlConnection.setRequestMethod("GET");
 httpUrlConnection.setRequestProperty("Authorization", "Bearer " + Constants.EGNYTEAPI_AUTH_TOKEN);

 httpUrlConnection.connect();

 if (httpUrlConnection.getResponseCode() == HttpURLConnection.HTTP_OK || httpUrlConnection.getResponseCode() == HttpURLConnection.HTTP_CREATED) {
  String fileName = fullPathWithSpaces.substring(fullPathWithSpaces.lastIndexOf('/') + 1);
  File tempDir = new File(System.getProperty("java.io.tmpdir") + "/" + Long.toString(System.nanoTime()));
  tempDir.mkdir();
  file = new File(tempDir.getAbsolutePath() + "/" + fileName);
  InputStream is = httpUrlConnection.getInputStream();
  Files.copy(is, file.toPath());
 }

 httpUrlConnection.disconnect();

 return file;
}

As simple as that! The method takes a full path to a file, and returns a java File object, saved in the system temp folder. If the file is not found, or there’s another error, the method simply returns a null File object. Note that the method will handle any URL-sensitive characters by percent-encoding them (https://en.wikipedia.org/wiki/Percent-encoding), so you won’t need to worry about spaces or other characters in the file name.

Another useful method is one that returns the listing of a file or folder on your Egnyte file server account:

public JSONObject listFileOrFolder(String fullFileOrFolderPathWithSpaces) throws Exception {
 JSONObject listing;

 URL url = UriBuilder.fromPath(Constants.EGNYTEAPI_BASE_URL + "fs/" + fullFileOrFolderPathWithSpaces).build().toURL();
 URLConnection urlConnection = url.openConnection();
 HttpURLConnection httpUrlConnection = (HttpURLConnection) urlConnection;
 httpUrlConnection.setRequestMethod("GET");
 httpUrlConnection.setRequestProperty("Authorization", "Bearer " + Constants.EGNYTEAPI_AUTH_TOKEN);

 httpUrlConnection.connect();

 if (httpUrlConnection.getResponseCode() == HttpURLConnection.HTTP_OK || httpUrlConnection.getResponseCode() == HttpURLConnection.HTTP_CREATED) {
  Scanner scanner = new Scanner(httpUrlConnection.getInputStream());
  scanner.useDelimiter("\\A");
  String returnJsonString = (scanner.hasNext() ? scanner.next() : "{}");
  scanner.close();
  listing = new JSONObject(returnJsonString);
 } else
  listing = new JSONObject();

 httpUrlConnection.disconnect();

 return listing;
}

This method will also take a full path to a folder or file (can include URL-sensitive characters, such as spaces) and will return either a null JSONObject if there was an error or if the file or folder was not found, or it will return a JSONObject of the listing returned by Egnyte.

The upload file method is a bit more complex, and took me a while to get right. Here it is:

public boolean uploadFile(String fullPathWithSpaces, File file) throws Exception {
 boolean returnValue = false;

 URL url = UriBuilder.fromPath(Constants.EGNYTEAPI_BASE_URL + "fs-content/" + fullPathWithSpaces).build().toURL();
 URLConnection urlConnection = url.openConnection();
 HttpURLConnection httpUrlConnection = (HttpURLConnection) urlConnection;
 httpUrlConnection.setRequestMethod("POST");
 httpUrlConnection.setDoOutput(true);
 httpUrlConnection.setRequestProperty("Authorization", "Bearer " + Constants.EGNYTEAPI_AUTH_TOKEN);

 String crlf = "\r\n", twoHyphens = "--", boundary = UUID.randomUUID().toString();

 httpUrlConnection.setRequestProperty("Content-Type", "multipart/form-data;boundary=" + boundary);

 DataOutputStream request = new DataOutputStream(httpUrlConnection.getOutputStream());

 request.writeBytes(twoHyphens + boundary + crlf);
 request.writeBytes("Content-Disposition: form-data; name=\"file\";filename=\"" + file.getName() + "\"" + crlf);
 request.writeBytes(crlf);

 InputStream fileInputStream = new FileInputStream(file);

 int bytesRead, bytesAvailable, bufferSize;
 byte[] buffer;
 int maxBufferSize = 1 * 1024 * 1024;

 bytesAvailable = fileInputStream.available();
 bufferSize = Math.min(bytesAvailable, maxBufferSize);
 buffer = new byte[bufferSize];
 bytesRead = fileInputStream.read(buffer, 0, bufferSize);
 while (bytesRead > 0) {
  request.write(buffer, 0, bufferSize);
  bytesAvailable = fileInputStream.available();
  bufferSize = Math.min(bytesAvailable, maxBufferSize);
  bytesRead = fileInputStream.read(buffer, 0, bufferSize);
 }
 fileInputStream.close();

 request.writeBytes(crlf);
 request.writeBytes(twoHyphens + boundary + twoHyphens + crlf);

 request.flush();
 request.close();

 if (httpUrlConnection.getResponseCode() == HttpURLConnection.HTTP_OK || httpUrlConnection.getResponseCode() == HttpURLConnection.HTTP_CREATED)
  returnValue = true;

 httpUrlConnection.disconnect();

 return returnValue;
}

The uploadFile() method takes a java File object, and a full path for where you want the file uploaded to on your Egnyte account. It then reads the file, and uploads it using HTTP multipart data.

Essentially using the methods above you have the basics down: listing a file or folder, uploading a file, and downloading a file. The same code from these can be used to do other actions too such as making a directory, or anything else that the Egnyte RESTful API allows.

Enjoy!