JBoss.orgCommunity Documentation

Chapter 39. Binary Values Processing

39.1. Configuration
39.2. Usage
39.3. Value implementations

Binary large object (BLOB) properties can be stored in two ways in the eXo JCR: in the database with items information or in an external storage on host file system. These options can be configured at workspace in the repository configuration file (repository-configuration.xml in portal and exo-jcr-config.xml in standalone mode). The database storage can't be completely disabled.

The first case is optimal for most of cases which you do not use very large values or/and do not have too many BLOBs. The configuration of the BLOBs size and BLOBs quantity in a repository depend on your database features and hardware.

The second case is to use an external values storage. The storage can be located on a built-in hard disk or on an attached storage. But in any cases, you should access to the storage as if it was a regular file(s). The external value storage is optional and can be enabled in a database configuration.

Note

eXo JCR Repository service configuration basics is discussed in Configuration

Database and workspace persistence storage configuration is discussed in JDBC Data Container config

Configuration details for External Value Storages.

In both of the cases, a developer can set/update the binary Property via Node.setProperty(String, InputStream), Property.setValue(InputStream) as described in the spec JSR-170. Also, there is the setter with a ready Value object (obtainer from ValueFactory.createValue(InputStream)).

An example of a specification usage.

// Set the property value with given stream content. 
Property binProp = node.setProperty("BinData", myDataStream);
// Get the property value stream. 
InputStream binStream = binProp.getStream();

// You may change the binary property value with a new Stream, all data will be replaced
// with the content from the new stream.
Property updatedBinProp = node.setProperty("BinData", newDataStream);
// Or update an obtained property
updatedBinProp.setValue(newDataStream);
// Or update using a Value object 
updatedBinProp.setValue(ValueFactory.createValue(newDataStream));
// Get the updated property value stream. 
InputStream newStream = updatedBinProp.getStream();

But if you need to update the property sequentially and with partial content, you have no choice but to edit the whole data stream outside and get it back to the repository each time. In case of really large-sized data, the application will be stuck and the productivity will decrease a lot. JCR stream setters will also check constraints and perform common validation each time.

There is a feature of the eXo JCR extension that can be used for binary values partial writing without frequent session level calls. The main idea is to use a value object obtained from the property as the storage of the property content while writing/reading during runtime.

According to the spec JSR-170, Value interface provides the state of property that can't be changed (edited). The eXo JCR core provides ReadableBinaryValue and EditableBinaryValue interfaces which themselves extend JCR Value. The interfaces allow the user to partially read and change a value content.

ReadableBinaryValue value can be casted from any value, i.e. String, Binary, Date etc.

// get the property value of type PropertyType.STRING 
ReadableBinaryValue extValue = (ReadableBinaryValue) node.getProperty("LargeText").getValue();
// read 200 bytes to a destStream from the position 1024 in the value content
OutputStream destStream = new FileOutputStream("MyTextFile.txt");
extValue.read(destStream, 200, 1024);

But EditableBinaryValue can be applied only to properties of type PropertyType.BINARY. In other cases, a cast to EditableBinaryValue will fail.

After the value has been edited, the EditableBinaryValue value can be applied to the property using the standard setters (Property.setValue(Value), Property.setValues(Value), Node.setProperty(String, Value) etc.). Only after the EditableBinaryValue has been set to the property, it can be obtained in this session by getters (Property.getValue(), Node.getProperty(String) etc.).

The user can obtain an EditableBinaryValue instance and fill it with data in an interaction manner (or any other appropriated to the targets) and return (set) the value to the property after the content will be done.

// get the property value for PropertyType.BINARY Property
EditableBinaryValue extValue = (EditableBinaryValue) node.getProperty("BinData").getValue();

// update length bytes from the stream starting from the position 1024 in existing Value data
extValue.update(dataInputStream, dataLength, 1024);

// apply the edited EditableBinaryValue to the Property
node.setProperty("BinData", extValue);

// save the Property to persistence
node.save();

A practical example of the iterative usage. In this example, the value is updated with data from the sequence of streams and after the update is done, the value will be applied to the property and be visible during the session.

// update length bytes from the stream starting from the particular 
// position in the existing Value data
int dpos = 1024;
while (source.dataAvailable()) {
  extValue.update(source.getInputStream(), source.getLength(), dpos);
  dpos = dpos + source.getLength();
}

// apply the edited EditableBinaryValue to the Property
node.setProperty("BinData", extValue);

ReadableBinaryValue has one method to read Value.

Read length bytes is counted from the binary value to the given position into the stream.

long read(OutputStream stream, long length, long position) throws IOException, RepositoryException ;

EditableBinaryValue has two methods to edit value.

Update with length bytes from the specified stream to this value data at a position. If the position is lower than 0, the IOException exception will be thrown. If the position is higher than the current Value length, the Value length will be increased at first to the size of position and length bytes will be added after the position.

void update(InputStream stream, long length, long position) throws IOException;

Set the length of the Value in bytes to the specified size. If the size is lower than 0, the IOException exception will be thrown. This operation can be used to extend or truncat the Value size. This method is used internally in the update operation in case of extending the size to the given position.

void setLength(long size) throws IOException;

An application can perform JCR binary operations more flexibly and will have less I/O and CPU usage using these methods.