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 2005/04/07 19:15:43 UTC

svn commit: r160421 - in incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core: SessionImpl.java xml/DocViewImportHandler.java xml/Importer.java xml/SysViewImportHandler.java xml/TargetImportHandler.java

Author: stefan
Date: Thu Apr  7 10:15:42 2005
New Revision: 160421

URL: http://svn.apache.org/viewcvs?view=rev&rev=160421
Log:
optimized value handling on import

Modified:
    incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/SessionImpl.java
    incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/xml/DocViewImportHandler.java
    incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/xml/Importer.java
    incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/xml/SysViewImportHandler.java
    incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/xml/TargetImportHandler.java

Modified: incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/SessionImpl.java
URL: http://svn.apache.org/viewcvs/incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/SessionImpl.java?view=diff&r1=160420&r2=160421
==============================================================================
--- incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/SessionImpl.java (original)
+++ incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/SessionImpl.java Thu Apr  7 10:15:42 2005
@@ -1057,7 +1057,7 @@
                               boolean skipBinary, boolean noRecurse)
             throws InvalidSerializedDataException, IOException,
             PathNotFoundException, RepositoryException {
-        boolean indenting = true;
+        boolean indenting = false;
         OutputFormat format = new OutputFormat("xml", "UTF-8", indenting);
         XMLSerializer serializer = new XMLSerializer(out, format);
         try {
@@ -1091,7 +1091,7 @@
     public void exportSysView(String absPath, OutputStream out,
                               boolean skipBinary, boolean noRecurse)
             throws IOException, PathNotFoundException, RepositoryException {
-        boolean indenting = true;
+        boolean indenting = false;
         OutputFormat format = new OutputFormat("xml", "UTF-8", indenting);
         XMLSerializer serializer = new XMLSerializer(out, format);
         try {

Modified: incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/xml/DocViewImportHandler.java
URL: http://svn.apache.org/viewcvs/incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/xml/DocViewImportHandler.java?view=diff&r1=160420&r2=160421
==============================================================================
--- incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/xml/DocViewImportHandler.java (original)
+++ incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/xml/DocViewImportHandler.java Thu Apr  7 10:15:42 2005
@@ -26,6 +26,7 @@
 
 import javax.jcr.PropertyType;
 import javax.jcr.RepositoryException;
+import java.io.IOException;
 import java.util.ArrayList;
 import java.util.Stack;
 
@@ -44,7 +45,7 @@
      */
     private final Stack stack = new Stack();
     // buffer used to merge adjacent character data
-    private StringBufferValue textHandler = new StringBufferValue();
+    private BufferedStringValue textHandler = new BufferedStringValue();
 
     /**
      * Constructs a new <code>DocViewImportHandler</code>.
@@ -57,34 +58,75 @@
     }
 
     /**
-     * Translates character data encountered in
-     * <code>{@link #characters(char[], int, int)}</code> into a
-     * <code>jcr:xmltext</code> child node with a <code>jcr:xmlcharacters</code>
-     * property.
+     * Appends the given character data to the internal buffer.
      *
-     * @param text
-     * @throws SAXException
+     * @param ch     the characters to be appended
+     * @param start  the index of the first character to append
+     * @param length the number of characters to append
+     * @throws SAXException if an error occurs
+     * @see #characters(char[], int, int)
+     * @see #ignorableWhitespace(char[], int, int)
+     * @see #processCharacters()
      */
-    private void onTextNode(StringBufferValue text)
+    private void appendCharacters(char ch[], int start, int length)
             throws SAXException {
-        String s = textHandler.retrieve();
-        if (s.trim().length() == 0) {
-            // ignore whitespace-only character data
-            log.debug("ignoring withespace character data: " + s);
-            return;
+        if (textHandler == null) {
+            textHandler = new BufferedStringValue();
         }
         try {
-            Importer.NodeInfo node =
-                    new Importer.NodeInfo(JCR_XMLTEXT, null, null, null);
-            Importer.TextValue[] values = new Importer.TextValue[]{text};
-            ArrayList props = new ArrayList();
-            Importer.PropInfo prop =
-                    new Importer.PropInfo(JCR_XMLCHARACTERS,
-                            PropertyType.STRING, values);
-            props.add(prop);
-            // call Importer
-            importer.startNode(node, props, nsContext);
-            importer.endNode(node);
+            textHandler.append(ch, start, length);
+        } catch (IOException ioe) {
+            String msg = "internal error while processing internal buffer data";
+            log.error(msg, ioe);
+            throw new SAXException(msg, ioe);
+        }
+    }
+
+    /**
+     * Translates character data reported by the
+     * <code>{@link #characters(char[], int, int)}</code> &
+     * <code>{@link #ignorableWhitespace(char[], int, int)}</code> SAX events
+     * into a  <code>jcr:xmltext</code> child node with one
+     * <code>jcr:xmlcharacters</code> property.
+     *
+     * @throws SAXException if an error occurs
+     * @see #appendCharacters(char[], int, int)
+     */
+    private void processCharacters()
+            throws SAXException {
+        try {
+            if (textHandler != null && textHandler.length() > 0) {
+                // there is character data that needs to be added to
+                // the current node
+
+                String text = textHandler.retrieve();
+                if (text.trim().length() == 0) {
+                    // ignore whitespace-only character data
+                    log.debug("ignoring withespace character data: " + text);
+                    return;
+                }
+
+                Importer.NodeInfo node =
+                        new Importer.NodeInfo(JCR_XMLTEXT, null, null, null);
+                Importer.TextValue[] values =
+                        new Importer.TextValue[]{textHandler};
+                ArrayList props = new ArrayList();
+                Importer.PropInfo prop =
+                        new Importer.PropInfo(JCR_XMLCHARACTERS,
+                                PropertyType.STRING, values);
+                props.add(prop);
+                // call Importer
+                importer.startNode(node, props, nsContext);
+                importer.endNode(node);
+
+                // reset handler
+                textHandler.dispose();
+                textHandler = null;
+            }
+        } catch (IOException ioe) {
+            String msg = "internal error while processing internal buffer data";
+            log.error(msg, ioe);
+            throw new SAXException(msg, ioe);
         } catch (RepositoryException re) {
             throw new SAXException(re);
         }
@@ -108,13 +150,8 @@
     public void startElement(String namespaceURI, String localName,
                              String qName, Attributes atts)
             throws SAXException {
-        if (textHandler != null && textHandler.length() > 0) {
-            // there is character data that needs to be added to the current node
-            onTextNode(textHandler);
-            // reset handler
-            textHandler.dispose();
-            textHandler = null;
-        }
+        // process buffered character data
+        processCharacters();
 
         try {
             QName nodeName = new QName(namespaceURI, localName);
@@ -196,13 +233,10 @@
     public void characters(char[] ch, int start, int length)
             throws SAXException {
         /**
-         * buffer character data; will be processed
-         * in endElement and startElement method
+         * buffer data reported by the characters event;
+         * will be processed on the next endElement or startElement event.
          */
-        if (textHandler == null) {
-            textHandler = new StringBufferValue();
-        }
-        textHandler.append(ch, start, length);
+        appendCharacters(ch, start, length);
     }
 
     /**
@@ -212,12 +246,9 @@
             throws SAXException {
         /**
          * buffer data reported by the ignorableWhitespace event;
-         * will be processed in endElement and startElement method
+         * will be processed on the next endElement or startElement event.
          */
-        if (textHandler == null) {
-            textHandler = new StringBufferValue();
-        }
-        textHandler.append(ch, start, length);
+        appendCharacters(ch, start, length);
     }
 
     /**
@@ -225,13 +256,9 @@
      */
     public void endElement(String namespaceURI, String localName, String qName)
             throws SAXException {
-        if (textHandler != null && textHandler.length() > 0) {
-            // there is character data that needs to be added to the current node
-            onTextNode(textHandler);
-            // reset handler
-            textHandler.dispose();
-            textHandler = null;
-        }
+        // process buffered character data
+        processCharacters();
+
         Importer.NodeInfo node = (Importer.NodeInfo) stack.peek();
         try {
             // call Importer

Modified: incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/xml/Importer.java
URL: http://svn.apache.org/viewcvs/incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/xml/Importer.java?view=diff&r1=160420&r2=160421
==============================================================================
--- incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/xml/Importer.java (original)
+++ incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/xml/Importer.java Thu Apr  7 10:15:42 2005
@@ -161,32 +161,26 @@
     public interface TextValue {
         /**
          * Returns the length of the serialized value.
+         *
          * @return the length of the serialized value
-         * @throws IllegalStateException if the serialized value is not
-         *                               available anymore (e.g. because it
-         *                               been discarded)
          * @throws IOException if an I/O error occurs
          */
-        public long length() throws IllegalStateException, IOException;
+        public long length() throws IOException;
 
         /**
          * Retrieves the serialized value.
+         *
          * @return the serialized value
-         * @throws IllegalStateException if the serialized value is not
-         *                               available anymore (e.g. because it
-         *                               been discarded)
          * @throws IOException if an I/O error occurs
          */
-        public String retrieve() throws IllegalStateException, IOException;
+        public String retrieve() throws IOException;
 
         /**
          * Returns a <code>Reader</code> for reading the serialized value.
+         *
          * @return a <code>Reader</code> for reading the serialized value.
-         * @throws IllegalStateException if the serialized value is not
-         *                               available anymore (e.g. because it
-         *                               been discarded)
          * @throws IOException if an I/O error occurs
          */
-        public Reader reader() throws IllegalStateException, IOException;
+        public Reader reader() throws IOException;
     }
 }

Modified: incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/xml/SysViewImportHandler.java
URL: http://svn.apache.org/viewcvs/incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/xml/SysViewImportHandler.java?view=diff&r1=160420&r2=160421
==============================================================================
--- incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/xml/SysViewImportHandler.java (original)
+++ incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/xml/SysViewImportHandler.java Thu Apr  7 10:15:42 2005
@@ -20,7 +20,6 @@
 import org.apache.jackrabbit.core.NamespaceResolver;
 import org.apache.jackrabbit.core.QName;
 import org.apache.jackrabbit.core.UnknownPrefixException;
-import org.apache.log4j.Logger;
 import org.xml.sax.Attributes;
 import org.xml.sax.SAXException;
 
@@ -37,8 +36,6 @@
  */
 class SysViewImportHandler extends TargetImportHandler {
 
-    private static Logger log = Logger.getLogger(SysViewImportHandler.class);
-
     /**
      * stack of ImportState instances; an instance is pushed onto the stack
      * in the startElement method every time a sv:node element is encountered;
@@ -179,20 +176,7 @@
             // sv:value element
 
             // reset temp fields
-            if (currentPropType == PropertyType.BINARY) {
-                // binary value; use temp-file backed value appender
-                try {
-                    currentPropValue = new CLOBValue();
-                } catch (IOException ioe) {
-                    String msg = "error while processing property value "
-                            + currentPropName;
-                    log.debug(msg, ioe);
-                    throw new SAXException(msg, ioe);
-                }
-            } else {
-                // 'normal' value; use StringBuffer-backed value appender
-                currentPropValue = new StringBufferValue();
-            }
+            currentPropValue = new BufferedStringValue();
         } else {
             throw new SAXException(new InvalidSerializedDataException("unexpected element found in system view xml document: "
                     + localName));

Modified: incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/xml/TargetImportHandler.java
URL: http://svn.apache.org/viewcvs/incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/xml/TargetImportHandler.java?view=diff&r1=160420&r2=160421
==============================================================================
--- incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/xml/TargetImportHandler.java (original)
+++ incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/xml/TargetImportHandler.java Thu Apr  7 10:15:42 2005
@@ -47,6 +47,11 @@
         this.nsContext = nsContext;
     }
 
+    /**
+     * Disposes all instances of <code>AppendableValue</code> contained in the
+     * given property info's value array.
+     * @param prop property info
+     */
     protected void disposePropertyValues(Importer.PropInfo prop) {
         Importer.TextValue[] vals = prop.getValues();
         for (int i = 0; i < vals.length; i++) {
@@ -54,8 +59,8 @@
                 try {
                     ((AppendableValue) vals[i]).dispose();
                 } catch (IOException ioe) {
-                    log.warn("error while disposing temporary value appender",
-                            ioe);
+                    log.warn("error while disposing temporary value", ioe);
+                    // fall through...
                 }
             }
         }
@@ -65,18 +70,36 @@
     /**
      * <code>AppendableValue</code> represents a serialized value that is
      * appendable.
+     * <p/>
+     * <b>Important:</b> Note that in order to free resources
+     * <code>{@link #dispose()}</code> should be called as soon as an an
+     * <code>AppendableValue</code> object is not used anymore.
      */
     public interface AppendableValue extends Importer.TextValue {
+        /**
+         * Append a portion of an array of characters.
+         * @param chars the characters to be appended
+         * @param start the index of the first character to append
+         * @param length the number of characters to append
+         * @throws IOException if an I/O error occurs
+         */
         public void append(char[] chars, int start, int length)
-                throws IllegalStateException, IOException;
+                throws IOException;
 
         /**
-         * @throws IOException
+         * Close this value. Once a value has been closed,
+         * further append() invocations will cause an IOException to be thrown.
+         *
+         * @throws IOException if an I/O error occurs
          */
         public void close() throws IOException;
 
         /**
-         * @throws IOException
+         * Dispose this value, i.e. free all bound resources. Once a value has
+         * been disposed, further method invocations will cause an IOException
+         * to be thrown.
+         *
+         * @throws IOException if an I/O error occurs
          */
         public void dispose() throws IOException;
     }
@@ -122,112 +145,108 @@
     }
 
     /**
-     * <code>StringBufferValue</code> represents an appendable serialized value
-     * that is internally backed by a <code>StringBuffer</code>.
+     * <code>BufferedStringValue</code> represents an appendable
+     * serialized value that is either buffered in-memory or backed
+     * by a temporary file if its size exceeds a certain limit.
+     * <p/>
+     * <b>Important:</b> Note that in order to free resources
+     * <code>{@link #dispose()}</code> should be called as soon as an
+     * <code>BufferedStringValue</code> instance is not used anymore.
      */
-    protected class StringBufferValue implements AppendableValue {
-
-        private final StringBuffer buffer;
+    protected class BufferedStringValue implements AppendableValue {
 
         /**
-         * Constructs a new empty <code>StringBufferValue</code>.
+         * max size for buffering data in memory
          */
-        protected StringBufferValue() {
-            buffer = new StringBuffer();
-        }
-
-        //--------------------------------------------------------< TextValue >
+        private static final int MAX_BUFFER_SIZE = 0x10000;
         /**
-         * {@inheritDoc}
+         * size of increment if capacity buffer needs to be enlarged
          */
-        public long length() {
-            return buffer.length();
-        }
-
+        private static final int BUFFER_INCREMENT = 0x2000;
         /**
-         * {@inheritDoc}
+         * in-memory buffer
          */
-        public String retrieve() {
-            return buffer.toString();
-        }
-
+        private char[] buffer;
         /**
-         * {@inheritDoc}
+         * current position within buffer (size of actual data in buffer)
          */
-        public Reader reader() {
-            return new StringReader(buffer.toString());
-        }
+        private int bufferPos;
 
-        //--------------------------------------------------< AppendableValue >
         /**
-         * {@inheritDoc}
+         * backing temporary file created when size of data exceeds
+         * MAX_BUFFER_SIZE
          */
-        public void append(char[] chars, int start, int length) {
-            buffer.append(chars, start, length);
-        }
-
+        private File tmpFile;
         /**
-         * {@inheritDoc}
+         * writer used to write to tmpFile; writer & tmpFile are always
+         * instantiated together, i.e. they are either both null or both not null.
          */
-        public void close() {
-            // nop
-        }
+        private Writer writer;
 
         /**
-         * {@inheritDoc}
+         * Constructs a new empty <code>BufferedStringValue</code>.
          */
-        public void dispose() {
-            buffer.setLength(0);
-        }
-    }
-
-    /**
-     * <code>CLOBValue</code> represents an appendable serialized value
-     * that is internally backed by a temporary file.
-     */
-    protected class CLOBValue implements AppendableValue {
-
-        private File tmpFile;
-        private Writer writer;
-
-        protected CLOBValue() throws IOException {
-            tmpFile = File.createTempFile("bin", null);
-            writer = new FileWriter(tmpFile);
+        protected BufferedStringValue() {
+            buffer = new char[0x2000];
+            bufferPos = 0;
+            tmpFile = null;
+            writer = null;
         }
 
         //--------------------------------------------------------< TextValue >
         /**
          * {@inheritDoc}
          */
-        public long length() throws IllegalStateException, IOException {
-            if (tmpFile == null) {
-                throw new IllegalStateException();
+        public long length() throws IOException {
+            if (buffer != null) {
+                return bufferPos;
+            } else if (tmpFile != null) {
+                writer.flush();
+                return tmpFile.length();
+            } else {
+                throw new IOException("this instance has already been disposed");
             }
-            return tmpFile.length();
         }
 
         /**
          * {@inheritDoc}
          */
-        public String retrieve() throws IllegalStateException, IOException {
-            Reader reader = reader();
-            char[] chunk = new char[8192];
-            int read;
-            StringBuffer buf = new StringBuffer();
-            while ((read = reader.read(chunk)) > -1) {
-                buf.append(chunk, 0, read);
+        public String retrieve() throws IOException {
+            if (buffer != null) {
+                return new String(buffer, 0, bufferPos);
+            } else if (tmpFile != null) {
+                if (length() > Integer.MAX_VALUE) {
+                    throw new IOException("size of value is too big, use reader()");
+                }
+                StringBuffer sb = new StringBuffer((int) tmpFile.length());
+                char[] chunk = new char[0x2000];
+                int read;
+                Reader reader = new FileReader(tmpFile);
+                try {
+                    while ((read = reader.read(chunk)) > -1) {
+                        sb.append(chunk, 0, read);
+                    }
+                } finally {
+                    reader.close();
+                }
+                return sb.toString();
+            } else {
+                throw new IOException("this instance has already been disposed");
             }
-            return buf.toString();
         }
 
         /**
          * {@inheritDoc}
          */
-        public Reader reader() throws IllegalStateException, IOException {
-            if (tmpFile == null) {
-                throw new IllegalStateException();
+        public Reader reader() throws IOException {
+            if (buffer != null) {
+                return new StringReader(new String(buffer, 0, bufferPos));
+            } else if (tmpFile != null) {
+                writer.flush();
+                return new FileReader(tmpFile);
+            } else {
+                throw new IOException("this instance has already been disposed");
             }
-            return new FileReader(tmpFile);
         }
 
         //--------------------------------------------------< AppendableValue >
@@ -235,20 +254,45 @@
          * {@inheritDoc}
          */
         public void append(char[] chars, int start, int length)
-                throws IllegalStateException, IOException {
-            if (writer == null) {
-                throw new IllegalStateException();
+                throws IOException {
+            if (buffer != null) {
+                if (bufferPos + length > MAX_BUFFER_SIZE) {
+                    // threshold for keeping data in memory exceeded;
+                    // create temp file and spool buffer contents
+                    tmpFile = File.createTempFile("txt", null);
+                    writer = new FileWriter(tmpFile);
+                    writer.write(buffer, 0, bufferPos);
+                    writer.write(chars, start, length);
+                    // reset fields
+                    buffer = null;
+                    bufferPos = 0;
+                } else {
+                    if (bufferPos + length > buffer.length) {
+                        // reallocate new buffer and spool old buffer contents
+                        char[] newBuffer = new char[buffer.length + BUFFER_INCREMENT];
+                        System.arraycopy(buffer, 0, newBuffer, 0, bufferPos);
+                        buffer = newBuffer;
+                    }
+                    System.arraycopy(chars, start, buffer, bufferPos, length);
+                    bufferPos += length;
+                }
+            } else if (tmpFile != null) {
+                writer.write(chars, start, length);
+            } else {
+                throw new IOException("this instance has already been disposed");
             }
-            writer.write(chars, start, length);
         }
 
         /**
          * {@inheritDoc}
          */
         public void close() throws IOException {
-            if (writer != null) {
+            if (buffer != null) {
+                // nop
+            } else if (tmpFile != null) {
                 writer.close();
-                writer = null;
+            } else {
+                throw new IOException("this instance has already been disposed");
             }
         }
 
@@ -256,10 +300,16 @@
          * {@inheritDoc}
          */
         public void dispose() throws IOException {
-            close();
-            if (tmpFile != null) {
+            if (buffer != null) {
+                buffer = null;
+                bufferPos = 0;
+            } else if (tmpFile != null) {
+                writer.close();
                 tmpFile.delete();
                 tmpFile = null;
+                writer = null;
+            } else {
+                throw new IOException("this instance has already been disposed");
             }
         }
     }