Re: How can I stream blobs to/from entities?
Re: How can I stream blobs to/from entities?
- Subject: Re: How can I stream blobs to/from entities?
- From: Paolo Sommaruga <email@hidden>
- Date: Wed, 14 Apr 2004 19:35:52 +0200
Hi Serge,
thanks for your feedback. I am rather interested on upload of large
blob by means of a JC. After that I have transferred the file from the
client side to the server side I tryied with the following remote
method in order to avoid the NSData use for memory saving (thanks to
Jonathan Rochkind for the hint how to get a JDBCContext)
public void clientSideRequestSaveDocument(String filename) {
EOObjectStoreCoordinator rootStore = null;
try {
Integer elementId = singleIntegerPrimaryKeyForObject(this); //
thanks to WOCode.com
File f = new File("/tmp/" + filename); // assume the file is
already in the server
int size = (int) f.length();
FileInputStream fin = new FileInputStream("/tmp/" + filename);
EOFetchSpecification fs = new
EOFetchSpecification("ElementoSemplice", null, null);
rootStore = (EOObjectStoreCoordinator)
editingContext().rootObjectStore();
rootStore.lock();
EODatabaseContext dbContext = (EODatabaseContext)
rootStore.objectStoreForFetchSpecification(fs);
EOAdaptor adaptor = dbContext.database().adaptor();
EOAdaptorContext adContext = dbContext.adaptorContext();
JDBCContext jcontext = (JDBCContext) adContext;
Connection con = jcontext.connection();
PreparedStatement pstmt = con.prepareStatement("update Elemento set
docfile=? where element_id=?");
pstmt.setInt(2, elementId.intValue());
pstmt.setBinaryStream(1, fin, size);
pstmt.executeUpdate();
}
catch(Exception e) {
e.printStackTrace();
}
finally {
rootStore.unlock();
}
}
The code works very well but only with file < 3MB size. With greater
size I need to increase the max jvm heap size. I can upload a file of
10 MB size only with -Xmx512m, which is too much memory for a single WO
instance for me
This is exactly the same behaviour which I have experimented with a
traditional code that use NSData, like
NSData resultData; // assume exists
editingContext().lock();
takeValueForKey(resultData, "docfile");
editingContext().saveChanges();
editingContext().unlock();
I had hoped that working directly at JDBC level the needed memory
amount would decrease. Instead the two codes seems actually to have the
same memory behaviour.
Is this a JDBC driver or a database issue ? I have tested with
PostgreSQL 7.4.1
Maybe some EO guru can give me some hint
Regards
Paolo Sommaruga
Il giorno 13/apr/04, alle 21:03, Serge Froment ha scritto:
> Hi Paolo,
>
> I have some code that works for me. Since it uses undocumented API, I
> can't garantee it works in all cases, though. A safer method would be
> to ignore WebObjects' database connections and create your own pool of
> JDBC connections.
>
> Anyway, here is a class SFGenericRecord
> (Stream-Friendly-Generic-Record) that implements a protected method
> blobValueForKey. This method fetches the database at JDBC level using
> WebObjects's existing connection and returns a java.sql.Blob.
>
> To use it:
>
> 1. Add the SFGenericRecord class to your project.
> 2. Derive you EOF class from SFGenericRecord instead of
> EOGenericRecord.
> 3. Don't mark your blob attribute as a class property (so that EOF will
> not fetch it).
> 4. Create an accessor that calls blobValueForKey (from superclass
> SFGenericRecord).
>
> To stream the blob to a WOResponse:
>
> Assume "build" is a EO instance that derives from SFGenericRecord and
> has an accessor outputFileData that calls
> blobValueForKey("outputFileData"):
>
> Blob blob = build.outputFileData();
> WOResponse response = new WOResponse();
> response.setHeader("data/binary", "Content-Type");
> response.setHeader("attachment;filename=" + build.outputFileName(),
> "Content-Disposition");
> response.setContentStream(blob.getBinaryStream(), 4096, (int)
> blob.length());
> return response;
>
> And now, the SFGenericRecord class:
>
> // Stream-Friendly subclass of EOGenericRecord
> public class SFGenericRecord extends EOGenericRecord
> {
> // constructors
> public SFGenericRecord()
> {
> super();
> }
> public SFGenericRecord(EOClassDescription classDescription)
> {
> super(classDescription);
> }
>
> // fetch a database blob using JDBC
> protected Blob storedBlobForKey(String key)
> {
> EOEntity entity =
> ((EOEntityClassDescription)classDescription()).entity();
> EODatabaseContext databaseContext =
> EODatabaseContext.registeredDatabaseContextForModel(entity.model(),
> editingContext());
> databaseContext.lock();
> try
> {
> return getBlob(entity, databaseContext, key);
> }
> catch (Exception e1)
> {
> if (databaseContext._isDroppedConnectionException(e1))
> {
> try
> {
> databaseContext.database().handleDroppedConnection();
> return getBlob(entity, databaseContext, key);
> }
> catch (Exception e2)
> {
> throw new NSForwardException(e2);
> }
> }
> else
> throw new NSForwardException(e1);
> }
> finally
> {
> databaseContext.unlock();
> }
> }
>
> // actual fetching for blob accessor
> private Blob getBlob(EOEntity entity, EODatabaseContext
> databaseContext, String key)
> throws SQLException
> {
> String attributeColumnName = entity.attributeNamed(key).columnName();
> StringBuffer query = new StringBuffer();
> query.append("SELECT ");
> query.append(attributeColumnName);
> query.append(" FROM ");
> query.append(entity.externalName());
> query.append(" WHERE ");
> NSDictionary primaryKey =
> EOUtilities.primaryKeyForObject(editingContext(), this);
> Enumeration attributeNames = primaryKey.allKeys().objectEnumerator();
> while (attributeNames.hasMoreElements())
> {
> String attributeName = (String) attributeNames.nextElement();
> query.append(entity.attributeNamed(attributeName).columnName());
> query.append(" = ");
> query.append(primaryKey.valueForKey(attributeName));
> if (attributeNames.hasMoreElements())
> query.append(" AND ");
> }
> EODatabaseChannel databaseChannel =
> databaseContext.availableChannel();
> EOAdaptorChannel adaptorChannel =
> databaseChannel.adaptorChannel();
> if(!adaptorChannel.isOpen())
> adaptorChannel.openChannel();
> Connection connection =
> ((JDBCContext)adaptorChannel.adaptorContext()).connection();
> Statement statement = connection.createStatement();
> ResultSet resultSet = statement.executeQuery(query.toString());
> if (resultSet.next())
> return resultSet.getBlob(attributeColumnName);
> else
> throw new SQLException("No record for query " + query);
> }
> }
>
> Le 13 avr. 04, ` 10:58, Paolo Sommaruga a icrit :
>
>> metto I have the same problema. Have you reached the WO JDBC
>> connection ?
>>
>
> Serge Froment
> http://www.serge-froment.com
> _______________________________________________
> webobjects-dev mailing list | email@hidden
> Help/Unsubscribe/Archives:
> http://www.lists.apple.com/mailman/listinfo/webobjects-dev
> Do not post admin requests to the list. They will be ignored.
_______________________________________________
webobjects-dev mailing list | email@hidden
Help/Unsubscribe/Archives: http://www.lists.apple.com/mailman/listinfo/webobjects-dev
Do not post admin requests to the list. They will be ignored.