[ 
https://issues.apache.org/jira/browse/DERBY-6341?page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel&focusedCommentId=13777419#comment-13777419
 ] 

Dag H. Wanvik commented on DERBY-6341:
--------------------------------------

I think I understand what the issue is here now: 

LOB persistence works ok, as Gary observed.

At retrieval time, OpenJPA code populates the entity by first performing a SQL 
query, then copies the LOB's input stream to the entity field (all in 
auto-commit mode). It loops through the result set, and at the end, the result 
set is auto-closed, which leads to a commit. The LOB's input stream is no 
longer accessible after that time (in conformance with the JDBC standard 
semantics).

I can mimic what happens in this little program:

    // c.setAutoCommit(false);
    Statement s = c.createStatement();
    ResultSet rs = s.executeQuery("select * from message"); // the entity table
    InputStream is = null;

    while (rs.next()) {
        // essentially what OpenJPA does 
        // is copy the "is" to the entity
        // instance here.                
        is = rs.getBinaryStrea,(3); 
   }                                


    // This is the point where the user's app tries to access the
    // entity's member field:

    final int len = 100;
    byte[] buffer = new byte[len];
    int bytesRead;
    
    while ((bytesRead = is.read(buffer, 0, len)) != -1) {
        System.out.println("read " + bytesRead);
    }

This will fail in "is.read" in the loop with:

Exception in thread "main" java.io.IOException: The object is already closed.
        at 
org.apache.derby.iapi.services.io.CloseFilterInputStream.checkIfClosed(CloseFilterInputStream.java:82)
        at 
org.apache.derby.iapi.services.io.CloseFilterInputStream.read(CloseFilterInputStream.java:70)
        at readlob.ReadLob.main(ReadLob.java:42)

I took at look at the Derby specialization code in OpenJPA, called
DerbyDictionary.java and added this overload:

    /** 
     * Obtain an {@link InputStream} by materializing it.
     * Unfortunately this will load entire BLOB into memory.  The
     * alternative of returning {@link ResultSet#getBinaryStream(int)}
     * as is provides true streaming but the stream can be consumed
     * only as long as {@link ResultSet} is open and Derby closes the
     * LOB (and its stream) when the result set is exhausted in
     * auto-commit mode.
     */
    @Override
    public InputStream getLOBStream(JDBCStore store, 
                                    ResultSet rs, 
                                    int column) 
            throws SQLException {
        Blob blob = rs.getBlob(column);

        if (blob == null) {
            return null;
        }

        ByteArrayOutputStream b = new ByteArrayOutputStream();
        ByteArrayInputStream bai = null;

        try {
            InputStream is = blob.getBinaryStream();
            int bytesRead;
            final int len = 128;
            byte[] buffer = new byte[len];
            
            while ( (bytesRead = is.read(buffer, 0, len)) != -1 ) {
                b.write(buffer, 0, bytesRead);
            }

            is.close();
            bai = new ByteArrayInputStream(b.toByteArray());
            b.close();
        } catch (IOException e) {
            throw new SQLException("IOException during LOB materialization", e);
        }
        
        return bai;
    }

I then recompiled OpenJPA 2.2.2 and ran my test (see Message.java and Main.java 
attached), and it worked. I'll see if I can contact the OpenJPA folks about 
this; maybe they can make a product fix for this issue.




                
> LOB streaming not working with ClientDriver - IOException: object already 
> closed
> --------------------------------------------------------------------------------
>
>                 Key: DERBY-6341
>                 URL: https://issues.apache.org/jira/browse/DERBY-6341
>             Project: Derby
>          Issue Type: Bug
>          Components: JDBC
>    Affects Versions: 10.10.1.1
>            Reporter: Gary Shank
>
> I have a small test program using OpenJPA v2.2.2 with Derby database 
> 10.10.1.1 and the Derby org.apache.derby.jdbc.ClientDriver.  I also tried 
> ClientDriver40.
> My entity is defined like this:
> @Entity(name = "BLOB_TEST")
> public class BlobTest implements java.io.Serializable {
>    public BlobTest() {}
>    @Id @Column(name = "PRIM_KEY", columnDefinition="VARCHAR(10)")
>    private String primKey = null;
>    public void setKey(String key) { primKey = key; }
>    public String getKey() { return primKey; }
>    @Persistent @Column(name = "DATA")
>    private InputStream data = null;
>    public void setData(InputStream data) { this.data = data; }
>    public InputStream getData() { return data; }
> }
> Putting data into the database works fine:
> EntityManager em = open(); // performs configuration and 
> emf.createEntityManager();
> em.getTransaction().begin();
> FileInputStream fis = new FileInputStream("someInputFile");
> BlobTest bt = new BlobTest();
> bt.setKey("1");
> bt.setData(fis);
> em.persist(bt);
> em.getTransaction().commit();
> em.close();
> Getting the data fails with "IOException: The object is already closed." when 
> any InputStream.read method is called:
> EntityManager em = open(); // performs configuration and 
> emf.createEntityManager();
> BlobTest bt = em.find(BlobTest.class, "1"); // the record is found
> InputStream is = bt.getData();
> while ( (bytesRead = is.read(buffer, 0, len)) != -1 )
> java.io.IOException: The object is already closed.
> at org.apache.derby.client.am.CloseFilterInputStream.read(Unknown Source)
> Getting the data works if I use JDBC directly like this:
> EntityManager em = open(); // performs configuration and 
> emf.createEntityManager();
> Connection conx = 
> (Connection)org.apache.openjpa.persistence.OpenJPAPersistence.cast(em).getConnection();
> PreparedStatement pstmt = conx.prepareStatement("select DATA from BLOB_TEST 
> where PRIM_KEY='1'");
> ResultSet rs = pstmt.executeQuery();
> InputStream is = rs.getBinaryStream(1);
> while ( (bytesRead = is.read(buffer, 0, len)) != -1 )
> Is this a bug or am I just doing something wrong?  My code has to work with 
> multiple databases so I can't really use JDBC directly - which is I opted for 
> using OpenJPA.  I'm not sure if this is an OpenJPA issue or Derby issue but, 
> at the moment, I'm assuming is a problem with the client driver.  By the way, 
> I did not test with the embedded driver since we need it to work with the 
> client driver.  I've looked at the following other issues:
> DERBY-3646 mentions "object already close" and the CloseFilterInputStream
> OPENJPA-1248 - LOB streaming does not work as expected
> OPENJPA-130 - use of InputStream for LOB streaming

--
This message is automatically generated by JIRA.
If you think it was sent incorrectly, please contact your JIRA administrators
For more information on JIRA, see: http://www.atlassian.com/software/jira

Reply via email to