blob: e26e2091d3c39106fb9854740602ba735da3f201 [file] [log] [blame]
/*
* Copyright (c) 2015 Eike Stepper (Berlin, Germany) and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Eike Stepper - initial API and implementation
*/
package org.eclipse.userstorage;
import org.eclipse.userstorage.spi.StorageCache;
import org.eclipse.userstorage.util.ConflictException;
import org.eclipse.userstorage.util.NoServiceException;
import org.eclipse.userstorage.util.NotFoundException;
import org.eclipse.userstorage.util.ProtocolException;
import java.io.DataInput;
import java.io.IOException;
import java.io.InputStream;
import java.util.Map;
/**
* Represents a piece of data that a {@link #getStorage() storage}
* maintains for the logged-in user under the associated {@link #getKey() key}.
* <p>
* The actual data can be read from the {@link InputStream} returned by the {@link #getContents() getContents()} method.
* It can be written by passing an {@link InputStream} to the {@link #setContents(InputStream) setContents()} method.
* <p>
* On top of this scalable I/O model a few methods are provided to get or set the blob's contents in
* more convenient formats, such as {@link DataInput#readUTF() UTF strings}, integers, or booleans.
* <p>
* In no case the contents of this blob are kept in memory. If the {@link #getStorage() storage} of this blob was
* created with a {@link StorageFactory#create(String, StorageCache) storage cache}
* the contents are possibly served from that cache. In any case all read and write access contacts the remote service
* behind storage's service, even if only to validate that a possibly cached version of the contents is still up-to-date,
* see {@link #getETag()}.
* <p>
*
* @author Eike Stepper
* @noextend This interface is not intended to be extended by clients.
* @noimplement This interface is not intended to be implemented by clients.
*/
public interface IBlob
{
/**
* Returns the storage of this blob.
*
* @return the storage of this blob, never <code>null</code>.
*/
public IStorage getStorage();
/**
* Returns the key of this blob.
*
* @return the key of this blob, never <code>null</code>.
*/
public String getKey();
/**
* Returns an immutable {@link Map} of the properties of this blob.
* <p>
* The properties represent arbitrary meta data about this blob that the service provider decides to provide.
* The only property that service providers are required to provide is the {@link #getETag() ETag} property.
* <p>
* The properties map is only updated during any of the getContents() or setContents() methods.
* If the {@link #getStorage() storage} of this blob was
* created with a {@link StorageFactory#create(String, StorageCache) storage cache}
* the properties are possibly served from that cache.
* <p>
*
* @return an immutable {@link Map} of the properties of this blob, never <code>null</code>.<p>
* @throws IllegalStateException if this blob is {@link #isDisposed() disposed}.<p>
*
* @see #getETag()
* @see #getContents()
* @see #setContents(InputStream)
*/
public Map<String, String> getProperties() throws IllegalStateException;
/**
* Returns the ETag of this blob.
* <p>
* The ETag of a blob is an opaque but unique value that the service provider assigns to a particular version
* (of the contents) of the blob. This value serves two purposes:
* <p>
* <ul>
* <li> If the {@link #getStorage() storage} of this blob was created with a
* {@link StorageFactory#create(String, StorageCache) storage cache}
* and the cache contains the current version of this blob
* all getContents() methods use the ETag to prevent the server from sending the blob's contents again.
* The cached contents are returned instead.
* <li> All setContents() methods use the ETag (if it is known) to prevent the server from storing conflicting
* changes. A conflicting change is updating a blob with new contents that is based on an out-dated ETag.
* </ul>
* <p>
*
* @return the ETag of this blob, or <code>null</code> if the ETag of this blob is currently not known.<p>
* @throws IllegalStateException if this blob is {@link #isDisposed() disposed}.<p>
*
* @see #setETag(String)
*/
public String getETag() throws IllegalStateException;
/**
* Sets the ETag of this blob.
* <p>
* The ETag of a blob is an opaque but unique value that the service provider assigns to a particular version
* (of the contents) of the blob. This value serves two purposes:
* <p>
* <ul>
* <li> If the {@link #getStorage() storage} of this blob was created with a
* {@link StorageFactory#create(String, StorageCache) storage cache}
* and the cache contains the current version of this blob
* all getContents() methods use the ETag to prevent the server from sending the blob's contents again.
* The cached contents are returned instead.
* <li> All setContents() methods use the ETag (if it is known) to prevent the server from storing conflicting
* changes. A conflicting change is updating a blob with new contents that is based on an out-dated ETag.
* </ul>
* <p>
* If the {@link #getStorage() storage} of this blob was created with a
* {@link StorageFactory#create(String, StorageCache) storage cache}
* and the new ETag is different from the cached ETag the cached blob is deleted locally.
* <p>
*
* @param eTag the new ETag of this blob, or <code>null</code> to reset the ETag.<p>
* @throws IllegalStateException if this blob is {@link #isDisposed() disposed}.<p>
*
* @see #getETag()
*/
public void setETag(String eTag) throws IllegalStateException;
/**
* Returns an {@link InputStream} that represents the current contents of this blob.
* <p>
* This method always contacts the server.
* <p>
* If the {@link #getStorage() storage} of this blob was created with a
* {@link StorageFactory#create(String, StorageCache) storage cache}
* and the cache contains the current version of this blob (i.e., the blob's ETag is up-to-date)
* the server will not send the blob's contents again. The cached contents are returned instead.
* If the cache does not contain the current version of this blob (i.e., the blob's ETag is out-of-date)
* the current remote contents are downloaded, cached, and returned by this method.
* <p>
* <b>Important note:</b>
* <p>
* The new contents will only be cached if (caching is enabled and) the
* returned input stream is fully read and closed.
* <p>
*
* @return an {@link InputStream} that represents the current contents of this blob, never <code>null</code>.<p>
* @throws IOException if remote I/O was unsuccessful. A {@link ProtocolException} may contain more information about protocol-specific problems.<p>
* @throws NoServiceException if the {@link #getStorage() storage} of this blob has no {@link IStorageService service} assigned.<p>
* @throws NotFoundException if this blob does not exist on the server.<p>
* @throws IllegalStateException if this blob is {@link #isDisposed() disposed}.<p>
*
* @see #setContents(InputStream)
* @see #getETag()
*/
public InputStream getContents() throws IOException, NoServiceException, NotFoundException, IllegalStateException;
/**
* Sets an {@link InputStream} that represents the new contents of this blob.
* <p>
* This method always contacts the server.
* <p>
* If this blob has an ETag it will be used to detect and avoid conflicts on the server side.
* The ETag of this blob can be {@link #setETag(String) set} to <code>null</code> if server-side conflict detection is not desired.
* Server-side conflicts are indicated by throwing a {@link ConflictException}.
* <p>
* If the {@link #getStorage() storage} of this blob was created with a
* {@link StorageFactory#create(String, StorageCache) storage cache}
* and the server successfully updated the blob (i.e., the blob's ETag was up-to-date)
* the cache will be updated with the new version of the contents.
* <p>
*
* @param in an {@link InputStream} that represents the new contents of this blob.<p>
* @return <code>true</code> if a new blob was created, <code>false</code> if an existing blob was updated.<p>
* @throws IOException if remote I/O was unsuccessful. A {@link ProtocolException} may contain more information about protocol-specific problems.<p>
* @throws NoServiceException if the {@link #getStorage() storage} of this blob has no {@link IStorageService service} assigned.<p>
* @throws ConflictException if the server detected a conflict and did not update the blob.<p>
* @throws IllegalStateException if this blob is {@link #isDisposed() disposed}.<p>
*
* @see #getContents()
* @see #setETag(String)
*/
public boolean setContents(InputStream in) throws IOException, NoServiceException, ConflictException, IllegalStateException;
/**
* Returns a {@link String} that represents the current contents of this blob.
* <p>
* This method is a convenient wrapper around the {@link #getContents()} method.
* It assumes that the binary contents of this blob represent a UTF-8 encoded text value.
* If this assumption is wrong (and this method can not detect this case) the results are unpredictable.
* Otherwise the semantics of this method are identical to the ones of {@link #getContents()}.
* <p>
*
* @return a {@link String} that represents the current contents of this blob, never <code>null</code>.<p>
* @throws IOException if remote I/O was unsuccessful. A {@link ProtocolException} may contain more information about protocol-specific problems.<p>
* @throws NoServiceException if the {@link #getStorage() storage} of this blob has no {@link IStorageService service} assigned.<p>
* @throws NotFoundException if this blob does not exist on the server.<p>
* @throws IllegalStateException if this blob is {@link #isDisposed() disposed}.<p>
*
* @see #getContents()
*/
public String getContentsUTF() throws IOException, NoServiceException, IllegalStateException, NotFoundException;
/**
* Sets a {@link String} that represents the new contents of this blob.
* <p>
* This method is a convenient wrapper around the {@link #setContents(InputStream)} method.
* The semantics of this method are identical to the ones of {@link #setContents(InputStream)}.
* <p>
*
* @param value a {@link String} that represents the new contents of this blob.<p>
* @return <code>true</code> if a new blob was created, <code>false</code> if an existing blob was updated.<p>
* @throws IOException if remote I/O was unsuccessful. A {@link ProtocolException} may contain more information about protocol-specific problems.<p>
* @throws NoServiceException if the {@link #getStorage() storage} of this blob has no {@link IStorageService service} assigned.<p>
* @throws ConflictException if the server detected a conflict and did not update the blob.<p>
* @throws IllegalStateException if this blob is {@link #isDisposed() disposed}.<p>
*
* @see #setContents(InputStream)
*/
public boolean setContentsUTF(String value) throws IOException, NoServiceException, ConflictException, IllegalStateException;
/**
* Returns a primitive int value that represents the current contents of this blob.
* <p>
* This method is a convenient wrapper around the {@link #getContents()} method.
* It assumes that the binary contents of this blob represent a UTF-8 encoded integer value.
* If this assumption is wrong (and this method can not detect this case) the results are unpredictable.
* Otherwise the semantics of this method are identical to the ones of {@link #getContents()}.
* <p>
*
* @return a primitive int value that represents the current contents of this blob.<p>
* @throws IOException if remote I/O was unsuccessful. A {@link ProtocolException} may contain more information about protocol-specific problems.<p>
* @throws NoServiceException if the {@link #getStorage() storage} of this blob has no {@link IStorageService service} assigned.<p>
* @throws NotFoundException if this blob does not exist on the server.<p>
* @throws IllegalStateException if this blob is {@link #isDisposed() disposed}.<p>
*
* @see #getContents()
*/
public int getContentsInt() throws IOException, NoServiceException, NotFoundException, IllegalStateException, NumberFormatException;
/**
* Sets a a primitive int value that represents the new contents of this blob.
* <p>
* This method is a convenient wrapper around the {@link #setContents(InputStream)} method.
* The semantics of this method are identical to the ones of {@link #setContents(InputStream)}.
* <p>
*
* @param value a a primitive int value that represents the new contents of this blob.<p>
* @return <code>true</code> if a new blob was created, <code>false</code> if an existing blob was updated.<p>
* @throws IOException if remote I/O was unsuccessful. A {@link ProtocolException} may contain more information about protocol-specific problems.<p>
* @throws NoServiceException if the {@link #getStorage() storage} of this blob has no {@link IStorageService service} assigned.<p>
* @throws ConflictException if the server detected a conflict and did not update the blob.<p>
* @throws IllegalStateException if this blob is {@link #isDisposed() disposed}.<p>
*
* @see #setContents(InputStream)
*/
public boolean setContentsInt(int value) throws IOException, NoServiceException, ConflictException, IllegalStateException;
/**
* Returns a primitive boolean value that represents the current contents of this blob.
* <p>
* This method is a convenient wrapper around the {@link #getContents()} method.
* It assumes that the binary contents of this blob represent a UTF-8 encoded boolean value.
* If this assumption is wrong (and this method can not detect this case) the results are unpredictable.
* Otherwise the semantics of this method are identical to the ones of {@link #getContents()}.
* <p>
*
* @return a primitive boolean value that represents the current contents of this blob.<p>
* @throws IOException if remote I/O was unsuccessful. A {@link ProtocolException} may contain more information about protocol-specific problems.<p>
* @throws NoServiceException if the {@link #getStorage() storage} of this blob has no {@link IStorageService service} assigned.<p>
* @throws NotFoundException if this blob does not exist on the server.<p>
* @throws IllegalStateException if this blob is {@link #isDisposed() disposed}.<p>
*
* @see #getContents()
*/
public boolean getContentsBoolean() throws IOException, NoServiceException, NotFoundException, IllegalStateException;
/**
* Sets a a primitive boolean value that represents the new contents of this blob.
* <p>
* This method is a convenient wrapper around the {@link #setContents(InputStream)} method.
* The semantics of this method are identical to the ones of {@link #setContents(InputStream)}.
* <p>
*
* @param value a a primitive boolean value that represents the new contents of this blob.<p>
* @return <code>true</code> if a new blob was created, <code>false</code> if an existing blob was updated.<p>
* @throws IOException if remote I/O was unsuccessful. A {@link ProtocolException} may contain more information about protocol-specific problems.<p>
* @throws NoServiceException if the {@link #getStorage() storage} of this blob has no {@link IStorageService service} assigned.<p>
* @throws ConflictException if the server detected a conflict and did not update the blob.<p>
* @throws IllegalStateException if this blob is {@link #isDisposed() disposed}.<p>
*
* @see #setContents(InputStream)
*/
public boolean setContentsBoolean(boolean value) throws IOException, NoServiceException, ConflictException, IllegalStateException;
/**
* Deletes this blob.
* <p>
* This method always contacts the server.
* <p>
* If this blob has an ETag it will be used to detect and avoid conflicts on the server side.
* The ETag of this blob can be {@link #setETag(String) set} to <code>null</code> if server-side conflict detection is not desired.
* Server-side conflicts are indicated by throwing a {@link ConflictException}.
* <p>
* If the {@link #getStorage() storage} of this blob was created with a
* {@link StorageFactory#create(String, StorageCache) storage cache}
* and the server successfully deleted the blob (i.e., the blob's ETag was up-to-date)
* the cache will be deleted, too.
* <p>
*
* @return <code>true</code> if this blob was successfully deleted from the server, <code>false</code> if it did not exist.<p>
* @throws IOException if remote I/O was unsuccessful. A {@link ProtocolException} may contain more information about protocol-specific problems.<p>
* @throws NoServiceException if the {@link #getStorage() storage} of this blob has no {@link IStorageService service} assigned.<p>
* @throws ConflictException if the server detected a conflict and did not update the blob.<p>
* @throws IllegalStateException if this blob is {@link #isDisposed() disposed}.<p>
*
* @see #setETag(String)
*/
public boolean delete() throws IOException, NoServiceException, ConflictException, IllegalStateException;
/**
* Returns <code>true</code> if this blob is disposed, <code>false</code> otherwise.
* <p>
* The only way for a blob to become disposed is when the storage of the {@link IStorage storage} of that blob
* is changed after that blob was created. Applications must call {@link IStorage#getBlob(String) getBlob()} to acquire
* a new, valid blob instance in that case.
* <p>
*
* @see IStorage#getBlob(String) getBlob()
*/
public boolean isDisposed();
}