You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@jackrabbit.apache.org by st...@apache.org on 2009/05/25 18:01:37 UTC

svn commit: r778445 - in /jackrabbit/trunk: jackrabbit-core/src/main/java/org/apache/jackrabbit/core/ jackrabbit-jcr-commons/src/main/java/org/apache/jackrabbit/value/

Author: stefan
Date: Mon May 25 16:01:36 2009
New Revision: 778445

URL: http://svn.apache.org/viewvc?rev=778445&view=rev
Log:
JCR-2056:  Binary interfaces (WIP...)



Added:
    jackrabbit/trunk/jackrabbit-jcr-commons/src/main/java/org/apache/jackrabbit/value/BinaryImpl.java
Modified:
    jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/PropertyImpl.java
    jackrabbit/trunk/jackrabbit-jcr-commons/src/main/java/org/apache/jackrabbit/value/BaseValue.java
    jackrabbit/trunk/jackrabbit-jcr-commons/src/main/java/org/apache/jackrabbit/value/BinaryValue.java
    jackrabbit/trunk/jackrabbit-jcr-commons/src/main/java/org/apache/jackrabbit/value/ValueFactoryImpl.java

Modified: jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/PropertyImpl.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/PropertyImpl.java?rev=778445&r1=778444&r2=778445&view=diff
==============================================================================
--- jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/PropertyImpl.java (original)
+++ jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/PropertyImpl.java Mon May 25 16:01:36 2009
@@ -754,10 +754,16 @@
         internalSetValue(internalValues, reqType);
     }
 
+    /**
+     * {@inheritDoc}
+     */
     public long getLength() throws RepositoryException {
         return getLength(internalGetValue());
     }
 
+    /**
+     * {@inheritDoc}
+     */
     public long[] getLengths() throws RepositoryException {
         InternalValue[] values = internalGetValues();
         long[] lengths = new long[values.length];
@@ -777,10 +783,23 @@
         return data.getPropertyDefinition();
     }
 
+    /**
+     * {@inheritDoc}
+     */
     public int getType() throws RepositoryException {
         return getPropertyState().getType();
     }
 
+    /**
+     * {@inheritDoc}
+     */
+    public boolean isMultiple() throws RepositoryException {
+        // check state of this instance
+        sanityCheck();
+
+        return data.getPropertyDefinition().isMultiple();
+    }
+
     //-----------------------------------------------------------------< Item >
     /**
      * {@inheritDoc}

Modified: jackrabbit/trunk/jackrabbit-jcr-commons/src/main/java/org/apache/jackrabbit/value/BaseValue.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-jcr-commons/src/main/java/org/apache/jackrabbit/value/BaseValue.java?rev=778445&r1=778444&r2=778445&view=diff
==============================================================================
--- jackrabbit/trunk/jackrabbit-jcr-commons/src/main/java/org/apache/jackrabbit/value/BaseValue.java (original)
+++ jackrabbit/trunk/jackrabbit-jcr-commons/src/main/java/org/apache/jackrabbit/value/BaseValue.java Mon May 25 16:01:36 2009
@@ -26,6 +26,7 @@
 import java.io.ByteArrayInputStream;
 import java.io.InputStream;
 import java.io.UnsupportedEncodingException;
+import java.io.IOException;
 import java.math.BigDecimal;
 import java.util.Calendar;
 
@@ -223,8 +224,15 @@
     public Binary getBinary()
             throws ValueFormatException, IllegalStateException,
             RepositoryException {
-        // TODO
-        throw new UnsupportedRepositoryOperationException("JCR-2056");
+        try {
+            // convert via string
+            return new BinaryImpl(new ByteArrayInputStream(getInternalString().getBytes(DEFAULT_ENCODING)));
+        } catch (UnsupportedEncodingException e) {
+            throw new RepositoryException(DEFAULT_ENCODING
+                    + " not supported on this platform", e);
+        } catch (IOException e) {
+            throw new RepositoryException("failed to create Binary instance", e);
+        }
     }
 
     /**

Added: jackrabbit/trunk/jackrabbit-jcr-commons/src/main/java/org/apache/jackrabbit/value/BinaryImpl.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-jcr-commons/src/main/java/org/apache/jackrabbit/value/BinaryImpl.java?rev=778445&view=auto
==============================================================================
--- jackrabbit/trunk/jackrabbit-jcr-commons/src/main/java/org/apache/jackrabbit/value/BinaryImpl.java (added)
+++ jackrabbit/trunk/jackrabbit-jcr-commons/src/main/java/org/apache/jackrabbit/value/BinaryImpl.java Mon May 25 16:01:36 2009
@@ -0,0 +1,187 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.jackrabbit.value;
+
+import org.apache.jackrabbit.util.TransientFileFactory;
+
+import javax.jcr.Binary;
+import javax.jcr.RepositoryException;
+import java.io.File;
+import java.io.InputStream;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.io.FileOutputStream;
+import java.io.FileInputStream;
+import java.io.RandomAccessFile;
+import java.io.FileNotFoundException;
+import java.io.ByteArrayInputStream;
+
+/**
+ * <code>BinaryImpl</code> implements the <code>Binary</code> interface.
+ */
+public class BinaryImpl implements Binary {
+
+    /**
+     * empty array
+     */
+    private static final byte[] EMPTY_BYTE_ARRAY = new byte[0];
+
+    /**
+     * max size for keeping tmp data in memory
+     */
+    private static final int MAX_BUFFER_SIZE = 0x10000;
+
+    /**
+     * underlying tmp file
+     */
+    private final File tmpFile;
+
+    /**
+     * buffer for small-sized data
+     */
+    private byte[] buffer = EMPTY_BYTE_ARRAY;
+
+    /**
+     * Creates a new <code>BinaryImpl</code> instance from an
+     * <code>InputStream</code>. The contents of the stream is spooled
+     * to a temporary file or to a byte buffer if its size is smaller than
+     * {@link #MAX_BUFFER_SIZE}.
+     * <p/>
+     * @param in stream to be represented as a <code>BLOBFileValue</code> instance
+     * @throws IOException if an error occurs while reading from the stream or
+     *                     writing to the temporary file
+     */
+    public BinaryImpl(InputStream in) throws IOException {
+        byte[] spoolBuffer = new byte[0x2000];
+        int read;
+        int len = 0;
+        OutputStream out = null;
+        File spoolFile = null;
+        try {
+            while ((read = in.read(spoolBuffer)) > 0) {
+                if (out != null) {
+                    // spool to temp file
+                    out.write(spoolBuffer, 0, read);
+                    len += read;
+                } else if (len + read > MAX_BUFFER_SIZE) {
+                    // threshold for keeping data in memory exceeded;
+                    // create temp file and spool buffer contents
+                    TransientFileFactory fileFactory = TransientFileFactory.getInstance();
+                    spoolFile = fileFactory.createTransientFile("bin", null, null);
+                    out = new FileOutputStream(spoolFile);
+                    out.write(buffer, 0, len);
+                    out.write(spoolBuffer, 0, read);
+                    buffer = null;
+                    len += read;
+                } else {
+                    // reallocate new buffer and spool old buffer contents
+                    byte[] newBuffer = new byte[len + read];
+                    System.arraycopy(buffer, 0, newBuffer, 0, len);
+                    System.arraycopy(spoolBuffer, 0, newBuffer, len, read);
+                    buffer = newBuffer;
+                    len += read;
+                }
+            }
+        } finally {
+            if (out != null) {
+                out.close();
+            }
+        }
+
+        // init fields
+        tmpFile = spoolFile;
+    }
+
+    /**
+     * Creates a new <code>BinaryImpl</code> instance from a
+     * <code>byte[]</code> array.
+     *
+     * @param buffer byte array to be represented as a <code>BinaryImpl</code>
+     *               instance
+     */
+    public BinaryImpl(byte[] buffer) {
+        if (buffer == null) {
+            throw new IllegalArgumentException("buffer must be non-null");
+        }
+        this.buffer = buffer;
+        tmpFile = null;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public InputStream getStream() throws RepositoryException {
+        if (tmpFile != null) {
+            try {
+                // this instance is backed by a temp file
+                return new FileInputStream(tmpFile);
+            } catch (FileNotFoundException e) {
+                throw new RepositoryException("already disposed");
+            }
+        } else {
+            // this instance is backed by an in-memory buffer
+            return new ByteArrayInputStream(buffer);
+        }
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public int read(byte[] b, long position) throws IOException, RepositoryException {
+        if (tmpFile != null) {
+            // this instance is backed by a temp file
+            RandomAccessFile raf = new RandomAccessFile(tmpFile, "r");
+            raf.seek(position);
+            return raf.read(b);
+        } else {
+            // this instance is backed by an in-memory buffer
+            int length = buffer.length - (int) position;
+            System.arraycopy(buffer, (int) position, b, 0, length);
+            return length;
+        }
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public long getSize() throws RepositoryException {
+        if (tmpFile != null) {
+            // this instance is backed by a temp file
+            if (tmpFile.exists()) {
+                return tmpFile.length();
+            } else {
+                return -1;
+            }
+        } else {
+            // this instance is backed by an in-memory buffer
+            return buffer.length;
+        }
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public void dispose() {
+        if (tmpFile != null) {
+            // this instance is backed by a temp file
+            tmpFile.delete();
+        } else {
+            // this instance is backed by an in-memory buffer
+            buffer = EMPTY_BYTE_ARRAY;
+        }
+    }
+}

Modified: jackrabbit/trunk/jackrabbit-jcr-commons/src/main/java/org/apache/jackrabbit/value/BinaryValue.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-jcr-commons/src/main/java/org/apache/jackrabbit/value/BinaryValue.java?rev=778445&r1=778444&r2=778445&view=diff
==============================================================================
--- jackrabbit/trunk/jackrabbit-jcr-commons/src/main/java/org/apache/jackrabbit/value/BinaryValue.java (original)
+++ jackrabbit/trunk/jackrabbit-jcr-commons/src/main/java/org/apache/jackrabbit/value/BinaryValue.java Mon May 25 16:01:36 2009
@@ -19,8 +19,8 @@
 import javax.jcr.PropertyType;
 import javax.jcr.RepositoryException;
 import javax.jcr.ValueFormatException;
+import javax.jcr.Binary;
 import java.io.ByteArrayInputStream;
-import java.io.ByteArrayOutputStream;
 import java.io.IOException;
 import java.io.InputStream;
 import java.io.UnsupportedEncodingException;
@@ -33,8 +33,7 @@
 
     public static final int TYPE = PropertyType.BINARY;
 
-    // those fields are mutually exclusive, i.e. only one can be non-null
-    private byte[] streamData = null;
+    private Binary bin = null;
     private String text = null;
 
     /**
@@ -48,23 +47,37 @@
     }
 
     /**
+     * Constructs a <code>BinaryValue</code> object based on a <code>Binary</code>.
+     *
+     * @param bin the bytes this <code>BinaryValue</code> should represent
+     */
+    public BinaryValue(Binary bin) {
+        super(TYPE);
+        this.bin = bin;
+    }
+
+    /**
      * Constructs a <code>BinaryValue</code> object based on a stream.
      *
      * @param stream the stream this <code>BinaryValue</code> should represent
      */
     public BinaryValue(InputStream stream) {
         super(TYPE);
-        this.stream = stream;
+        try {
+            bin = new BinaryImpl(stream);
+        } catch (IOException e) {
+            throw new IllegalArgumentException("specified stream cannot be read", e);
+        }
     }
 
     /**
-     * Constructs a <code>BinaryValue</code> object based on a stream.
+     * Constructs a <code>BinaryValue</code> object based on a byte array.
      *
-     * @param data the stream this <code>BinaryValue</code> should represent
+     * @param data the bytes this <code>BinaryValue</code> should represent
      */
     public BinaryValue(byte[] data) {
         super(TYPE);
-        streamData = data;
+        bin = new BinaryImpl(data);
     }
 
     /**
@@ -85,18 +98,11 @@
         if (obj instanceof BinaryValue) {
             BinaryValue other = (BinaryValue) obj;
             if (text == other.text && stream == other.stream
-                    && streamData == other.streamData) {
+                    && bin == other.bin) {
                 return true;
             }
-            // stream, streamData and text are mutually exclusive,
-            // i.e. only one of them can be non-null
-            if (stream != null) {
-                return stream.equals(other.stream);
-            } else if (streamData != null) {
-                return streamData.equals(other.streamData);
-            } else {
-                return text.equals(other.text);
-            }
+            return (text != null && text.equals(other.text))
+                    || (bin != null && bin.equals(other.bin));
         }
         return false;
     }
@@ -124,46 +130,20 @@
     public String getInternalString()
             throws ValueFormatException, RepositoryException {
         // build text value if necessary
-        if (streamData != null) {
+        if (text == null) {
             try {
-                text = new String(streamData, DEFAULT_ENCODING);
-            } catch (UnsupportedEncodingException e) {
-                throw new RepositoryException(DEFAULT_ENCODING
-                        + " not supported on this platform", e);
-            }
-            streamData = null;
-        } else if (stream != null) {
-            try {
-                ByteArrayOutputStream out = new ByteArrayOutputStream();
-                byte[] buffer = new byte[8192];
-                int read;
-                while ((read = stream.read(buffer)) > 0) {
-                    out.write(buffer, 0, read);
-                }
-                byte[] data = out.toByteArray();
-                text = new String(data, DEFAULT_ENCODING);
+                byte[] bytes = new byte[(int) bin.getSize()];
+                bin.read(bytes, 0);
+                text = new String(bytes, DEFAULT_ENCODING);
             } catch (UnsupportedEncodingException e) {
                 throw new RepositoryException(DEFAULT_ENCODING
                         + " not supported on this platform", e);
             } catch (IOException e) {
-                throw new RepositoryException("conversion from stream to string failed", e);
-            } finally {
-                try {
-                    if (stream != null) {
-                        stream.close();
-                    }
-                } catch (IOException e) {
-                    // ignore
-                }
+                throw new RepositoryException("failed to retrieve binary data", e);
             }
-            stream = null;
         }
 
-        if (text != null) {
-            return text;
-        } else {
-            throw new ValueFormatException("empty value");
-        }
+        return text;
     }
 
     //----------------------------------------------------------------< Value >
@@ -174,20 +154,36 @@
             throws IllegalStateException, RepositoryException {
         setStreamConsumed();
 
-        // build stream value if necessary
-        if (streamData != null) {
-            stream = new ByteArrayInputStream(streamData);
-            streamData = null;
-        } else if (text != null) {
+        if (bin != null) {
+            return bin.getStream();
+        } else {
             try {
-                stream = new ByteArrayInputStream(text.getBytes(DEFAULT_ENCODING));
+                return new ByteArrayInputStream(text.getBytes(DEFAULT_ENCODING));
             } catch (UnsupportedEncodingException e) {
                 throw new RepositoryException(DEFAULT_ENCODING
                         + " not supported on this platform", e);
             }
-            text = null;
+        }
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public Binary getBinary()
+            throws ValueFormatException, IllegalStateException,
+            RepositoryException {
+
+        if (bin == null) {
+            try {
+                bin = new BinaryImpl(new ByteArrayInputStream(text.getBytes(DEFAULT_ENCODING)));
+            } catch (UnsupportedEncodingException e) {
+                throw new RepositoryException(DEFAULT_ENCODING
+                        + " not supported on this platform", e);
+            } catch (IOException e) {
+                throw new RepositoryException("failed to retrieve binary data", e);
+            }
         }
 
-        return super.getStream();
+        return bin;
     }
 }

Modified: jackrabbit/trunk/jackrabbit-jcr-commons/src/main/java/org/apache/jackrabbit/value/ValueFactoryImpl.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-jcr-commons/src/main/java/org/apache/jackrabbit/value/ValueFactoryImpl.java?rev=778445&r1=778444&r2=778445&view=diff
==============================================================================
--- jackrabbit/trunk/jackrabbit-jcr-commons/src/main/java/org/apache/jackrabbit/value/ValueFactoryImpl.java (original)
+++ jackrabbit/trunk/jackrabbit-jcr-commons/src/main/java/org/apache/jackrabbit/value/ValueFactoryImpl.java Mon May 25 16:01:36 2009
@@ -17,6 +17,7 @@
 package org.apache.jackrabbit.value;
 
 import java.io.InputStream;
+import java.io.IOException;
 import java.math.BigDecimal;
 import java.util.Calendar;
 
@@ -151,12 +152,22 @@
         return val;
     }
 
+    /**
+     * {@inheritDoc}
+     */
     public Binary createBinary(InputStream stream) throws RepositoryException {
-        throw new UnsupportedRepositoryOperationException("JCR-2056");
+        try {
+            return new BinaryImpl(stream);
+        } catch (IOException e) {
+            throw new RepositoryException("failed to create Binary instance", e);
+        }
     }
 
+    /**
+     * {@inheritDoc}
+     */
     public Value createValue(Binary value) {
-        throw new UnsupportedOperationException("JCR-2056");
+        return new BinaryValue(value);
     }
 
     /**