Add JavaDocs
diff --git a/org.eclipse.userstorage/src/org/eclipse/userstorage/IBlob.java b/org.eclipse.userstorage/src/org/eclipse/userstorage/IBlob.java
index 04032de..4572ccc 100644
--- a/org.eclipse.userstorage/src/org/eclipse/userstorage/IBlob.java
+++ b/org.eclipse.userstorage/src/org/eclipse/userstorage/IBlob.java
@@ -146,7 +146,7 @@
    * returned input stream is fully read and closed.
    * <p>
    *
-   * @return an {@link InputStream} that represents the current contents of this blob, <code>null</code> if this blob does not exist on the server.<p>
+   * @return an {@link InputStream} that represents the current contents of this blob, or <code>null</code> if this blob does not exist on the server.<p>
    * @throws IOException if remote I/O was unsuccessful. A {@link ProtocolException} may contain more information about protocol-specific problems.<p>
    * @throws IllegalStateException if this blob is {@link #isDisposed() disposed}.<p>
    *
diff --git a/org.eclipse.userstorage/src/org/eclipse/userstorage/internal/StorageSpace.java b/org.eclipse.userstorage/src/org/eclipse/userstorage/internal/StorageSpace.java
index 0848468..7a1452a 100644
--- a/org.eclipse.userstorage/src/org/eclipse/userstorage/internal/StorageSpace.java
+++ b/org.eclipse.userstorage/src/org/eclipse/userstorage/internal/StorageSpace.java
@@ -155,7 +155,7 @@
     {
       if (contents == Blob.NOT_MODIFIED)
       {
-        return cache.getInputStream(applicationToken, key);
+        return cache.internalGetInputStream(applicationToken, key);
       }
 
       if (contents == null)
diff --git a/org.eclipse.userstorage/src/org/eclipse/userstorage/spi/ICredentialsProvider.java b/org.eclipse.userstorage/src/org/eclipse/userstorage/spi/ICredentialsProvider.java
index 9d610f9..e6e1254 100644
--- a/org.eclipse.userstorage/src/org/eclipse/userstorage/spi/ICredentialsProvider.java
+++ b/org.eclipse.userstorage/src/org/eclipse/userstorage/spi/ICredentialsProvider.java
@@ -14,9 +14,19 @@
 import org.eclipse.userstorage.internal.Credentials;
 
 /**
+ * Provides the user's credentials for a given {@link IStorage storage}.
+ *
  * @author Eike Stepper
  */
 public interface ICredentialsProvider
 {
+  /**
+   * Provides the user's credentials for the given {@link IStorage storage}.
+   * <p>
+   *
+   * @param storage the storage for which to provide the user's credentials, must not be <code>null</code>.<p>
+   * @return the user's credentials for the given {@link IStorage storage},
+   *         or <code>null</code> as an indication to cancel the authentication process.<p>
+   */
   public Credentials provideCredentials(IStorage storage);
 }
diff --git a/org.eclipse.userstorage/src/org/eclipse/userstorage/spi/StorageCache.java b/org.eclipse.userstorage/src/org/eclipse/userstorage/spi/StorageCache.java
index c11ce95..66ea8bf 100644
--- a/org.eclipse.userstorage/src/org/eclipse/userstorage/spi/StorageCache.java
+++ b/org.eclipse.userstorage/src/org/eclipse/userstorage/spi/StorageCache.java
@@ -10,7 +10,9 @@
  */
 package org.eclipse.userstorage.spi;
 
+import org.eclipse.userstorage.IBlob;
 import org.eclipse.userstorage.IStorage;
+import org.eclipse.userstorage.IStorageSpace;
 import org.eclipse.userstorage.internal.InternalStorageCache;
 
 import java.io.IOException;
@@ -20,14 +22,29 @@
 import java.util.Map;
 
 /**
+ * Locally caches the {@link IBlob#getProperties() properties} and {@link IBlob#getContents() contents}
+ * of {@link IBlob blobs}.
+ * <p>
+ *
  * @author Eike Stepper
  */
 public abstract class StorageCache extends InternalStorageCache
 {
+  /**
+   * Constructs this cache.
+   */
   public StorageCache()
   {
+    // Do nothing.
   }
 
+  /**
+   * Returns the storage of this cache.
+   *
+   * @return the storage of this cache, never <code>null</code>.<p>
+   *
+   * @see IStorageSpace#setStorage(IStorage)
+   */
   @Override
   public final IStorage getStorage()
   {
@@ -35,7 +52,14 @@
   }
 
   /**
-   * Subclasses may override.
+   * This method is called when an application has called {@link IStorageSpace#setStorage(IStorage) setStorage()}
+   * on a {@link IStorageSpace storage space} that was created with a cache.
+   * <p>
+   *
+   * @param oldStorage the old storage of this cache.<p>
+   * @param newStorage the new storage of this cache.<p>
+   *
+   * @see IStorageSpace#setStorage(IStorage)
    */
   @Override
   protected void storageChanged(IStorage oldStorage, IStorage newStorage)
@@ -43,21 +67,93 @@
     // Do nothing.
   }
 
+  /**
+   * Returns an {@link Iterator} over the {@link IBlob#getKey() keys} of all blobs with
+   * the given {@link IStorageSpace#getApplicationToken() application token} that are cached in this cache.
+   * <p>
+   *
+   * @param applicationToken the {@link IStorageSpace#getApplicationToken() application token} for which to return all keys,
+   *        must not be <code>null</code>.<p>
+   * @return an {@link Iterator} over the {@link IBlob#getKey() keys} of all blobs with
+   *         the given applicationToken that are cached in this cache, never <code>null</code>.<p>
+   * @throws IOException if local I/O was unsuccessful.<p>
+   */
   @Override
   public abstract Iterator<String> getKeys(String applicationToken) throws IOException;
 
+  /**
+   * Loads the properties of the blob with the given {@link IStorageSpace#getApplicationToken() application token}
+   * and {@link IBlob#getKey() key} from this cache into the given properties map.
+   * <p>
+   *
+   * @param applicationToken the {@link IStorageSpace#getApplicationToken() application token} for which to load the properties,
+   *        must not be <code>null</code>.<p>
+   * @param key the {@link IBlob#getKey() key} for which to load the properties,
+   *        must not be <code>null</code>.<p>
+   * @param properties the properties map into which to load the properties,
+   *        must not be <code>null</code>.<p>
+   * @throws IOException if local I/O was unsuccessful.<p>
+   */
   @Override
   protected abstract void loadProperties(String applicationToken, String key, Map<String, String> properties) throws IOException;
 
+  /**
+   * Saves the given properties of the blob with the given {@link IStorageSpace#getApplicationToken() application token}
+   * and {@link IBlob#getKey() key} into this cache.
+   * <p>
+   *
+   * @param applicationToken the {@link IStorageSpace#getApplicationToken() application token} for which to save the properties,
+   *        must not be <code>null</code>.<p>
+   * @param key the {@link IBlob#getKey() key} for which to save the properties,
+   *        must not be <code>null</code>.<p>
+   * @param properties the properties map to save,
+   *        must not be <code>null</code>.<p>
+   * @throws IOException if local I/O was unsuccessful.<p>
+   */
   @Override
   protected abstract void saveProperties(String applicationToken, String key, Map<String, String> properties) throws IOException;
 
+  /**
+   * Returns an {@link InputStream} that represents the cached contents of the blob with the given
+   * {@link IStorageSpace#getApplicationToken() application token} and {@link IBlob#getKey() key}.
+   * <p>
+   *
+   * @param applicationToken the {@link IStorageSpace#getApplicationToken() application token} for which to return the contents stream,
+   *        must not be <code>null</code>.<p>
+   * @param key the {@link IBlob#getKey() key} for which to return the contents stream,
+   *        must not be <code>null</code>.<p>
+   * @return an {@link InputStream} that represents the cached contents of this blob, never <code>null</code>.<p>
+   * @throws IOException if local I/O was unsuccessful, including the case that the contents disappeared from this cache.<p>
+   */
   @Override
   protected abstract InputStream getInputStream(String applicationToken, String key) throws IOException;
 
+  /**
+   * Returns an {@link OutputStream} that represents the cached contents of the blob with the given
+   * {@link IStorageSpace#getApplicationToken() application token} and {@link IBlob#getKey() key}.
+   * <p>
+   *
+   * @param applicationToken the {@link IStorageSpace#getApplicationToken() application token} for which to return the contents stream,
+   *        must not be <code>null</code>.<p>
+   * @param key the {@link IBlob#getKey() key} for which to return the contents stream,
+   *        must not be <code>null</code>.<p>
+   * @return an {@link OutputStream} that represents the cached contents of this blob, never <code>null</code>.<p>
+   * @throws IOException if local I/O was unsuccessful.<p>
+   */
   @Override
   protected abstract OutputStream getOutputStream(String applicationToken, String key) throws IOException;
 
+  /**
+   * Deletes the blob with the given {@link IStorageSpace#getApplicationToken() application token} and {@link IBlob#getKey() key}
+   * from this cache.
+   * <p>
+   *
+   * @param applicationToken the {@link IStorageSpace#getApplicationToken() application token} of the blob to delete,
+   *        must not be <code>null</code>.<p>
+   * @param key the {@link IBlob#getKey() key} of the blob to delete,
+   *        must not be <code>null</code>.<p>
+   * @throws IOException if local I/O was unsuccessful.<p>
+   */
   @Override
   protected abstract void delete(String applicationToken, String key) throws IOException;
 }
diff --git a/org.eclipse.userstorage/src/org/eclipse/userstorage/util/FileStorageCache.java b/org.eclipse.userstorage/src/org/eclipse/userstorage/util/FileStorageCache.java
index 1233f0b..c064a3a 100644
--- a/org.eclipse.userstorage/src/org/eclipse/userstorage/util/FileStorageCache.java
+++ b/org.eclipse.userstorage/src/org/eclipse/userstorage/util/FileStorageCache.java
@@ -10,6 +10,8 @@
  */
 package org.eclipse.userstorage.util;
 
+import org.eclipse.userstorage.IBlob;
+import org.eclipse.userstorage.IStorageSpace;
 import org.eclipse.userstorage.internal.util.IOUtil;
 import org.eclipse.userstorage.internal.util.StringUtil;
 import org.eclipse.userstorage.spi.StorageCache;
@@ -27,29 +29,59 @@
 import java.util.Set;
 
 /**
+ * A local file system based {@link StorageCache cache}.
+ *
  * @author Eike Stepper
  */
 public class FileStorageCache extends StorageCache
 {
-  private static final String PROPERTIES = ".properties";
+  /**
+   * The file name extension of the files that contain the blobs' properties.
+   */
+  protected static final String PROPERTIES = ".properties";
 
   private final File folder;
 
+  /**
+   * Constructs this cache with a randomly named folder in the user's temp directory.
+   *
+   * @see #getFolder()
+   */
   public FileStorageCache()
   {
     this(createTempFolder());
   }
 
+  /**
+   * Constructs this cache with the given folder.
+   *
+   * @param folder the folder in which to create this cache, must not be <code>null</code>.<p>
+   *
+   * @see #getFolder()
+   */
   public FileStorageCache(File folder)
   {
+    if (folder == null)
+    {
+      throw new IllegalArgumentException("Folder is null");
+    }
+
     this.folder = folder;
   }
 
+  /**
+   * Returns the file system folder of this cache.
+   *
+   * @return the file system folder of this cache, never <code>null</code>.<p>
+   */
   public final File getFolder()
   {
     return folder;
   }
 
+  /**
+   * {@inheritDoc}
+   */
   @Override
   public Iterator<String> getKeys(String applicationToken) throws IOException
   {
@@ -74,6 +106,9 @@
     return keys.iterator();
   }
 
+  /**
+   * {@inheritDoc}
+   */
   @Override
   protected void loadProperties(String applicationToken, String key, Map<String, String> properties) throws IOException
   {
@@ -101,6 +136,9 @@
     }
   }
 
+  /**
+   * {@inheritDoc}
+   */
   @Override
   protected void saveProperties(String applicationToken, String key, Map<String, String> properties) throws IOException
   {
@@ -127,6 +165,9 @@
     }
   }
 
+  /**
+   * {@inheritDoc}
+   */
   @Override
   protected InputStream getInputStream(String applicationToken, String key) throws IOException
   {
@@ -134,6 +175,9 @@
     return new FileInputStream(file);
   }
 
+  /**
+  * {@inheritDoc}
+  */
   @Override
   protected OutputStream getOutputStream(String applicationToken, String key) throws IOException
   {
@@ -142,6 +186,9 @@
     return new FileOutputStream(file);
   }
 
+  /**
+   * {@inheritDoc}
+   */
   @Override
   protected void delete(String applicationToken, String key) throws IOException
   {
@@ -149,6 +196,23 @@
     getFile(applicationToken, key, null).delete();
   }
 
+  /**
+   * Returns the {@link File} object that represents the blob with the given
+   * {@link IStorageSpace#getApplicationToken() application token} and {@link IBlob#getKey() key} into this cache.
+   * <p>
+   * This cache stores the properties and the contents of a blob in two different files.
+   * Which of the two files is returned is determined by the value of the extension parameter.
+   * <p>
+   *
+   * @param applicationToken the {@link IStorageSpace#getApplicationToken() application token} for which to return the file,
+   *        must not be <code>null</code>.<p>
+   * @param key the {@link IBlob#getKey() key} for which to return the file,
+   *        must not be <code>null</code>.<p>
+   * @param extension the value of {@link #PROPERTIES} to return the file that contains the blob's properties,
+   *        or <code>null</code> to return the file that contains the blob's contents.<p>
+   * @return the file that contains the blob's properties if the value of {@link #PROPERTIES} was passed into the <code>extension</code> parameter,
+   *         or the file that contains the blob's contents if <code>null</code> was passed into the <code>extension</code> parameter.<p>
+   */
   protected File getFile(String applicationToken, String key, String extension)
   {
     return new File(new File(folder, applicationToken), key + StringUtil.safe(extension));