/** * The contents of this file are subject to the license and copyright * detailed in the LICENSE and NOTICE files at the root of the source * tree and available online at * * http://www.dspace.org/license/ */ package org.dspace.content; import java.io.IOException; import java.io.InputStream; import java.sql.SQLException; import java.util.ArrayList; import java.util.List; import org.apache.log4j.Logger; import org.dspace.authorize.AuthorizeException; import org.dspace.authorize.AuthorizeManager; import org.dspace.core.ConfigurationManager; import org.dspace.core.Constants; import org.dspace.core.Context; import org.dspace.core.LogManager; import org.dspace.event.Event; import org.dspace.storage.bitstore.BitstreamStorageManager; import org.dspace.storage.rdbms.DatabaseManager; import org.dspace.storage.rdbms.TableRow; import org.dspace.storage.rdbms.TableRowIterator; /** * Class representing bitstreams stored in the DSpace system. *
* When modifying the bitstream metadata, changes are not reflected in the
* database until update
is called. Note that you cannot alter
* the contents of a bitstream; you need to create a new bitstream.
*
* @author Robert Tansley
* @version $Revision$
*/
public class Bitstream extends DSpaceObject
{
/** log4j logger */
private static Logger log = Logger.getLogger(Bitstream.class);
/** Our context */
private Context bContext;
/** The row in the table representing this bitstream */
private TableRow bRow;
/** The bitstream format corresponding to this bitstream */
private BitstreamFormat bitstreamFormat;
/** Flag set when data is modified, for events */
private boolean modified;
/** Flag set when metadata is modified, for events */
private boolean modifiedMetadata;
/**
* Private constructor for creating a Bitstream object based on the contents
* of a DB table row.
*
* @param context
* the context this object exists in
* @param row
* the corresponding row in the table
* @throws SQLException
*/
Bitstream(Context context, TableRow row) throws SQLException
{
bContext = context;
bRow = row;
// Get the bitstream format
bitstreamFormat = BitstreamFormat.find(context, row
.getIntColumn("bitstream_format_id"));
if (bitstreamFormat == null)
{
// No format: use "Unknown"
bitstreamFormat = BitstreamFormat.findUnknown(context);
// Panic if we can't find it
if (bitstreamFormat == null)
{
throw new IllegalStateException("No Unknown bitstream format");
}
}
// Cache ourselves
context.cache(this, row.getIntColumn("bitstream_id"));
modified = false;
modifiedMetadata = false;
clearDetails();
}
/**
* Get a bitstream from the database. The bitstream metadata is loaded into
* memory.
*
* @param context
* DSpace context object
* @param id
* ID of the bitstream
*
* @return the bitstream, or null if the ID is invalid.
* @throws SQLException
*/
public static Bitstream find(Context context, int id) throws SQLException
{
// First check the cache
Bitstream fromCache = (Bitstream) context
.fromCache(Bitstream.class, id);
if (fromCache != null)
{
return fromCache;
}
TableRow row = DatabaseManager.find(context, "bitstream", id);
if (row == null)
{
if (log.isDebugEnabled())
{
log.debug(LogManager.getHeader(context, "find_bitstream",
"not_found,bitstream_id=" + id));
}
return null;
}
// not null, return Bitstream
if (log.isDebugEnabled())
{
log.debug(LogManager.getHeader(context, "find_bitstream",
"bitstream_id=" + id));
}
return new Bitstream(context, row);
}
public static Bitstream[] findAll(Context context) throws SQLException
{
TableRowIterator tri = DatabaseManager.queryTable(context, "bitstream",
"SELECT * FROM bitstream");
Listnull
sets the type
* of this bitstream to "unknown".
*
* @param f
* the format of this bitstream, or null
for
* unknown
* @throws SQLException
*/
public void setFormat(BitstreamFormat f) throws SQLException
{
// FIXME: Would be better if this didn't throw an SQLException,
// but we need to find the unknown format!
if (f == null)
{
// Use "Unknown" format
bitstreamFormat = BitstreamFormat.findUnknown(bContext);
}
else
{
bitstreamFormat = f;
}
// Remove user type description
bRow.setColumnNull("user_format_description");
// Update the ID in the table row
bRow.setColumn("bitstream_format_id", bitstreamFormat.getID());
modified = true;
}
/**
* Update the bitstream metadata. Note that the content of the bitstream
* cannot be changed - for that you need to create a new bitstream.
*
* @throws SQLException
* @throws AuthorizeException
*/
public void update() throws SQLException, AuthorizeException
{
// Check authorisation
AuthorizeManager.authorizeAction(bContext, this, Constants.WRITE);
log.info(LogManager.getHeader(bContext, "update_bitstream",
"bitstream_id=" + getID()));
if (modified)
{
bContext.addEvent(new Event(Event.MODIFY, Constants.BITSTREAM, getID(), null));
modified = false;
}
if (modifiedMetadata)
{
bContext.addEvent(new Event(Event.MODIFY_METADATA, Constants.BITSTREAM, getID(), getDetails()));
modifiedMetadata = false;
clearDetails();
}
DatabaseManager.update(bContext, bRow);
}
/**
* Delete the bitstream, including any mappings to bundles
*
* @throws SQLException
*/
void delete() throws SQLException
{
boolean oracle = false;
if ("oracle".equals(ConfigurationManager.getProperty("db.name")))
{
oracle = true;
}
// changed to a check on remove
// Check authorisation
//AuthorizeManager.authorizeAction(bContext, this, Constants.DELETE);
log.info(LogManager.getHeader(bContext, "delete_bitstream",
"bitstream_id=" + getID()));
bContext.addEvent(new Event(Event.DELETE, Constants.BITSTREAM, getID(), String.valueOf(getSequenceID())));
// Remove from cache
bContext.removeCached(this, getID());
// Remove policies
AuthorizeManager.removeAllPolicies(bContext, this);
// Remove references to primary bitstreams in bundle
String query = "update bundle set primary_bitstream_id = ";
query += (oracle ? "''" : "Null") + " where primary_bitstream_id = ? ";
DatabaseManager.updateQuery(bContext,
query, bRow.getIntColumn("bitstream_id"));
// Remove bitstream itself
BitstreamStorageManager.delete(bContext, bRow
.getIntColumn("bitstream_id"));
}
/**
* Bitstreams are only logically deleted (via a flag in the database).
* This method allows us to verify is the bitstream is still valid
*
* @return true if the bitstream has been deleted
*/
boolean isDeleted() throws SQLException
{
String query = "select count(*) as mycount from Bitstream where deleted = '1' and bitstream_id = ? ";
TableRowIterator tri = DatabaseManager.query(bContext, query, bRow.getIntColumn("bitstream_id"));
long count = 0;
try
{
TableRow r = tri.next();
count = r.getLongColumn("mycount");
}
finally
{
// close the TableRowIterator to free up resources
if (tri != null)
{
tri.close();
}
}
return count == 1;
}
/**
* Retrieve the contents of the bitstream
*
* @return a stream from which the bitstream can be read.
* @throws IOException
* @throws SQLException
* @throws AuthorizeException
*/
public InputStream retrieve() throws IOException, SQLException,
AuthorizeException
{
// Maybe should return AuthorizeException??
AuthorizeManager.authorizeAction(bContext, this, Constants.READ);
return BitstreamStorageManager.retrieve(bContext, bRow
.getIntColumn("bitstream_id"));
}
/**
* Get the bundles this bitstream appears in
*
* @return array of Bundle
s this bitstream appears in
* @throws SQLException
*/
public Bundle[] getBundles() throws SQLException
{
// Get the bundle table rows
TableRowIterator tri = DatabaseManager.queryTable(bContext, "bundle",
"SELECT bundle.* FROM bundle, bundle2bitstream WHERE " +
"bundle.bundle_id=bundle2bitstream.bundle_id AND " +
"bundle2bitstream.bitstream_id= ? ",
bRow.getIntColumn("bitstream_id"));
// Build a list of Bundle objects
List