You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@sling.apache.org by cz...@apache.org on 2015/01/15 19:46:58 UTC

svn commit: r1652218 - in /sling/trunk/bundles/jcr/resource: ./ src/main/java/org/apache/sling/jcr/resource/ src/main/java/org/apache/sling/jcr/resource/internal/ src/main/java/org/apache/sling/jcr/resource/internal/helper/

Author: cziegeler
Date: Thu Jan 15 18:46:58 2015
New Revision: 1652218

URL: http://svn.apache.org/r1652218
Log:
SLING-4307 : Avoid caching JCR property values

Modified:
    sling/trunk/bundles/jcr/resource/pom.xml
    sling/trunk/bundles/jcr/resource/src/main/java/org/apache/sling/jcr/resource/JcrModifiablePropertyMap.java
    sling/trunk/bundles/jcr/resource/src/main/java/org/apache/sling/jcr/resource/JcrPropertyMap.java
    sling/trunk/bundles/jcr/resource/src/main/java/org/apache/sling/jcr/resource/JcrResourceUtil.java
    sling/trunk/bundles/jcr/resource/src/main/java/org/apache/sling/jcr/resource/internal/JcrModifiableValueMap.java
    sling/trunk/bundles/jcr/resource/src/main/java/org/apache/sling/jcr/resource/internal/NodeUtil.java
    sling/trunk/bundles/jcr/resource/src/main/java/org/apache/sling/jcr/resource/internal/helper/JcrPropertyMapCacheEntry.java

Modified: sling/trunk/bundles/jcr/resource/pom.xml
URL: http://svn.apache.org/viewvc/sling/trunk/bundles/jcr/resource/pom.xml?rev=1652218&r1=1652217&r2=1652218&view=diff
==============================================================================
--- sling/trunk/bundles/jcr/resource/pom.xml (original)
+++ sling/trunk/bundles/jcr/resource/pom.xml Thu Jan 15 18:46:58 2015
@@ -92,9 +92,9 @@
                             org.apache.jackrabbit.api.observation
                         </DynamicImport-Package>
 
-                        <!-- Include URL support from Jackrabbit -->
+                        <!-- Include utility classes from Jackrabbit JCR Commons -->
                         <Embed-Dependency>
-                            jackrabbit-jcr-commons;inline="org/apache/jackrabbit/util/ISO9075.*|org/apache/jackrabbit/name/QName.*|org/apache/jackrabbit/util/XMLChar.*|org/apache/jackrabbit/util/Text.*",
+                            jackrabbit-jcr-commons;inline="org/apache/jackrabbit/util/ISO8601.*|org/apache/jackrabbit/util/ISO9075.*|org/apache/jackrabbit/name/QName.*|org/apache/jackrabbit/util/XMLChar.*|org/apache/jackrabbit/util/Text.*",
                         </Embed-Dependency>
 
                         <Sling-Namespaces>

Modified: sling/trunk/bundles/jcr/resource/src/main/java/org/apache/sling/jcr/resource/JcrModifiablePropertyMap.java
URL: http://svn.apache.org/viewvc/sling/trunk/bundles/jcr/resource/src/main/java/org/apache/sling/jcr/resource/JcrModifiablePropertyMap.java?rev=1652218&r1=1652217&r2=1652218&view=diff
==============================================================================
--- sling/trunk/bundles/jcr/resource/src/main/java/org/apache/sling/jcr/resource/JcrModifiablePropertyMap.java (original)
+++ sling/trunk/bundles/jcr/resource/src/main/java/org/apache/sling/jcr/resource/JcrModifiablePropertyMap.java Thu Jan 15 18:46:58 2015
@@ -25,6 +25,7 @@ import java.util.Set;
 
 import javax.jcr.Node;
 import javax.jcr.RepositoryException;
+import javax.jcr.Value;
 
 import org.apache.sling.api.resource.PersistableValueMap;
 import org.apache.sling.api.resource.PersistenceException;
@@ -91,7 +92,7 @@ public final class JcrModifiableProperty
         readFully();
         final Object oldValue = this.get(key);
         try {
-            this.cache.put(key, new JcrPropertyMapCacheEntry(value, getNode().getSession()));
+            this.cache.put(key, new JcrPropertyMapCacheEntry(value, this.getNode()));
         } catch (final RepositoryException re) {
             throw new IllegalArgumentException("Value for key " + key + " can't be put into node: " + value, re);
         }
@@ -162,7 +163,7 @@ public final class JcrModifiableProperty
             if ( this.changedProperties.contains(NodeUtil.MIXIN_TYPES) ) {
                 if ( cache.containsKey(NodeUtil.MIXIN_TYPES) ) {
                     final JcrPropertyMapCacheEntry entry = cache.get(NodeUtil.MIXIN_TYPES);
-                    NodeUtil.handleMixinTypes(node, entry.values);
+                    NodeUtil.handleMixinTypes(node, entry.convertToType(String[].class, node, dynamicClassLoader));
                 } else {
                     // remove all mixin types!
                     NodeUtil.handleMixinTypes(node, null);
@@ -174,10 +175,10 @@ public final class JcrModifiableProperty
                 if ( !NodeUtil.MIXIN_TYPES.equals(name) ) {
                     if ( cache.containsKey(key) ) {
                         final JcrPropertyMapCacheEntry entry = cache.get(key);
-                        if ( entry.isMulti ) {
-                            node.setProperty(name, entry.values);
+                        if ( entry.isArray() ) {
+                            node.setProperty(name, entry.convertToType(Value[].class, node, dynamicClassLoader));
                         } else {
-                            node.setProperty(name, entry.values[0]);
+                            node.setProperty(name, entry.convertToType(Value.class, node, dynamicClassLoader));
                         }
                     } else {
                         if ( node.hasProperty(name) ) {

Modified: sling/trunk/bundles/jcr/resource/src/main/java/org/apache/sling/jcr/resource/JcrPropertyMap.java
URL: http://svn.apache.org/viewvc/sling/trunk/bundles/jcr/resource/src/main/java/org/apache/sling/jcr/resource/JcrPropertyMap.java?rev=1652218&r1=1652217&r2=1652218&view=diff
==============================================================================
--- sling/trunk/bundles/jcr/resource/src/main/java/org/apache/sling/jcr/resource/JcrPropertyMap.java (original)
+++ sling/trunk/bundles/jcr/resource/src/main/java/org/apache/sling/jcr/resource/JcrPropertyMap.java Thu Jan 15 18:46:58 2015
@@ -18,36 +18,25 @@
  */
 package org.apache.sling.jcr.resource;
 
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.Serializable;
-import java.lang.reflect.Array;
-import java.math.BigDecimal;
-import java.util.ArrayList;
 import java.util.Calendar;
 import java.util.Collection;
 import java.util.Collections;
 import java.util.Date;
 import java.util.Iterator;
 import java.util.LinkedHashMap;
-import java.util.List;
 import java.util.Map;
 import java.util.Set;
 
 import javax.jcr.Node;
 import javax.jcr.Property;
 import javax.jcr.PropertyIterator;
-import javax.jcr.PropertyType;
 import javax.jcr.RepositoryException;
 import javax.jcr.Value;
-import javax.jcr.ValueFormatException;
 
 import org.apache.jackrabbit.util.ISO9075;
 import org.apache.jackrabbit.util.Text;
 import org.apache.sling.api.resource.ValueMap;
 import org.apache.sling.jcr.resource.internal.helper.JcrPropertyMapCacheEntry;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
 
 /**
  * An implementation of the value map based on a JCR node.
@@ -56,9 +45,6 @@ import org.slf4j.LoggerFactory;
 public class JcrPropertyMap
     implements ValueMap {
 
-    /** default logger */
-    private static Logger LOGGER = LoggerFactory.getLogger(JcrPropertyMap.class);
-
     /** The underlying node. */
     private final Node node;
 
@@ -74,10 +60,10 @@ public class JcrPropertyMap
     /** keep all prefixes for escaping */
     private String[] namespacePrefixes;
 
-    private final ClassLoader dynamicClassLoader;
+    final ClassLoader dynamicClassLoader;
 
     /**
-     * Constructor
+     * Create a new JCR property map based on a node.
      * @param node The underlying node.
      */
     public JcrPropertyMap(final Node node) {
@@ -85,7 +71,7 @@ public class JcrPropertyMap
     }
 
     /**
-     * Constructor
+     * Create a new JCR property map based on a node.
      * @param node The underlying node.
      * @param dynamicCL Dynamic class loader for loading serialized objects.
      * @since 2.0.8
@@ -131,7 +117,7 @@ public class JcrPropertyMap
         if ( entry == null ) {
             return null;
         }
-        return convertToType(entry, type);
+        return entry.convertToType(type, this.node, this.dynamicClassLoader);
     }
 
     /**
@@ -164,7 +150,7 @@ public class JcrPropertyMap
     public Object get(final Object aKey) {
         final String key = checkKey(aKey.toString());
         final JcrPropertyMapCacheEntry entry = this.read(key);
-        final Object value = (entry == null ? null : entry.getDefaultValueOrNull());
+        final Object value = (entry == null ? null : entry.getPropertyValueOrNull());
         return value;
     }
 
@@ -278,9 +264,9 @@ public class JcrPropertyMap
                 entry = new JcrPropertyMapCacheEntry(prop);
                 cache.put(key, entry);
 
-                final Object defaultValue = entry.getDefaultValue();
+                final Object defaultValue = entry.getPropertyValue();
                 if (defaultValue != null) {
-                    valueCache.put(key, entry.getDefaultValue());
+                    valueCache.put(key, entry.getPropertyValue());
                 }
             }
             return entry;
@@ -444,159 +430,6 @@ public class JcrPropertyMap
 
     // ---------- Implementation helper
 
-    @SuppressWarnings("unchecked")
-    private <T> T convertToType(final JcrPropertyMapCacheEntry entry, Class<T> type) {
-        T result = null;
-
-        try {
-            final boolean array = type.isArray();
-
-            if (entry.isMulti) {
-
-                if (array) {
-
-                    result = (T) convertToArray(entry,
-                        type.getComponentType());
-
-                } else if (entry.values.length > 0) {
-
-                    result = convertToType(entry, -1, entry.values[0], type);
-
-                }
-
-            } else {
-
-                if (array) {
-
-                    result = (T) convertToArray(entry,
-                            type.getComponentType());
-
-                } else {
-
-                    result = convertToType(entry, -1, entry.values[0], type);
-
-                }
-            }
-
-        } catch (ValueFormatException vfe) {
-            LOGGER.info("converToType: Cannot convert value of " + entry.getDefaultValueOrNull()
-                + " to " + type, vfe);
-        } catch (RepositoryException re) {
-            LOGGER.info("converToType: Cannot get value of " + entry.getDefaultValueOrNull(), re);
-        }
-
-        // fall back to nothing
-        return result;
-    }
-
-    private <T> T[] convertToArray(final JcrPropertyMapCacheEntry entry, Class<T> type)
-    throws ValueFormatException, RepositoryException {
-        List<T> values = new ArrayList<T>();
-        for (int i = 0; i < entry.values.length; i++) {
-            T value = convertToType(entry, i, entry.values[i], type);
-            if (value != null) {
-                values.add(value);
-            }
-        }
-
-        @SuppressWarnings("unchecked")
-        T[] result = (T[]) Array.newInstance(type, values.size());
-
-        return values.toArray(result);
-    }
-
-    @SuppressWarnings("unchecked")
-    private <T> T convertToType(final JcrPropertyMapCacheEntry entry,
-                                final int index,
-                                final Value jcrValue,
-                                final Class<T> type)
-    throws ValueFormatException, RepositoryException {
-        Object defaultValue = entry.getDefaultValue();
-        if ( type.isInstance(defaultValue) ) {
-            return (T) defaultValue;
-        }
-
-        if (String.class == type) {
-            return (T) jcrValue.getString();
-
-        } else if (Byte.class == type) {
-            return (T) Byte.valueOf((byte) jcrValue.getLong());
-
-        } else if (Short.class == type) {
-            return (T) Short.valueOf((short) jcrValue.getLong());
-
-        } else if (Integer.class == type) {
-            return (T) Integer.valueOf((int) jcrValue.getLong());
-
-        } else if (Long.class == type) {
-            if (jcrValue.getType() == PropertyType.BINARY) {
-                if (index == -1) {
-                    return (T) Long.valueOf(entry.property.getLength());
-                }
-                return (T) Long.valueOf(entry.property.getLengths()[index]);
-            }
-            return (T) Long.valueOf(jcrValue.getLong());
-
-        } else if (Float.class == type) {
-            return (T) Float.valueOf((float) jcrValue.getDouble());
-
-        } else if (Double.class == type) {
-            return (T) Double.valueOf(jcrValue.getDouble());
-
-        } else if (BigDecimal.class == type) {
-            return (T) jcrValue.getDecimal();
-
-        } else if (Boolean.class == type) {
-            return (T) Boolean.valueOf(jcrValue.getBoolean());
-
-        } else if (Date.class == type) {
-            return (T) jcrValue.getDate().getTime();
-
-        } else if (Calendar.class == type) {
-            return (T) jcrValue.getDate();
-
-        } else if (Value.class == type) {
-            return (T) jcrValue;
-
-        } else if (Property.class == type) {
-            return (T) entry.property;
-
-        } else if (ObjectInputStream.class == type) {
-            if ( jcrValue.getType() == PropertyType.BINARY ) {
-                try {
-                    return (T) new ObjectInputStream(jcrValue.getBinary().getStream(), this.dynamicClassLoader);
-                } catch (IOException ioe) {
-                    // ignore and use fallback
-                }
-            }
-        } else if (Serializable.class.isAssignableFrom(type)
-                && jcrValue.getType() == PropertyType.BINARY) {
-            ObjectInputStream ois = null;
-            try {
-                ois = new ObjectInputStream(jcrValue.getBinary().getStream(), this.dynamicClassLoader);
-                final Object obj = ois.readObject();
-                if ( type.isInstance(obj) ) {
-                    return (T)obj;
-                }
-            } catch (ClassNotFoundException cnfe) {
-                 // ignore and use fallback
-            } catch (IOException ioe) {
-                // ignore and use fallback
-            } finally {
-                if ( ois != null ) {
-                    try {
-                        ois.close();
-                    } catch (IOException ignore) {
-                        // ignore
-                    }
-                }
-            }
-        }
-
-        // fallback in case of unsupported type
-        return null;
-    }
-
     private Class<?> normalizeClass(Class<?> type) {
         if (Calendar.class.isAssignableFrom(type)) {
             type = Calendar.class;
@@ -614,7 +447,7 @@ public class JcrPropertyMap
 
 		Map<String, Object> transformedEntries = new LinkedHashMap<String, Object>(map.size());
 		for ( Map.Entry<String, JcrPropertyMapCacheEntry> entry : map.entrySet() )
-			transformedEntries.put(entry.getKey(), entry.getValue().getDefaultValueOrNull());
+			transformedEntries.put(entry.getKey(), entry.getValue().getPropertyValueOrNull());
 
 		return transformedEntries;
 	}
@@ -641,30 +474,4 @@ public class JcrPropertyMap
         sb.append("}]");
         return sb.toString();
     }
-
-
-    /**
-     * This is an extended version of the object input stream which uses the
-     * thread context class loader.
-     */
-    private static class ObjectInputStream extends java.io.ObjectInputStream {
-
-        private ClassLoader classloader;
-
-        public ObjectInputStream(final InputStream in, final ClassLoader classLoader) throws IOException {
-            super(in);
-            this.classloader = classLoader;
-        }
-
-        /**
-         * @see java.io.ObjectInputStream#resolveClass(java.io.ObjectStreamClass)
-         */
-        @Override
-        protected Class<?> resolveClass(java.io.ObjectStreamClass classDesc) throws IOException, ClassNotFoundException {
-            if ( this.classloader != null ) {
-                return this.classloader.loadClass(classDesc.getName());
-            }
-            return super.resolveClass(classDesc);
-        }
-    }
 }

Modified: sling/trunk/bundles/jcr/resource/src/main/java/org/apache/sling/jcr/resource/JcrResourceUtil.java
URL: http://svn.apache.org/viewvc/sling/trunk/bundles/jcr/resource/src/main/java/org/apache/sling/jcr/resource/JcrResourceUtil.java?rev=1652218&r1=1652217&r2=1652218&view=diff
==============================================================================
--- sling/trunk/bundles/jcr/resource/src/main/java/org/apache/sling/jcr/resource/JcrResourceUtil.java (original)
+++ sling/trunk/bundles/jcr/resource/src/main/java/org/apache/sling/jcr/resource/JcrResourceUtil.java Thu Jan 15 18:46:58 2015
@@ -130,8 +130,8 @@ public class JcrResourceUtil {
      * @return the value or null if not convertible to a valid PropertyType
      * @throws RepositoryException in case of error, accessing the Repository
      */
-    public static Value createValue(Object value, Session session)
-            throws RepositoryException {
+    public static Value createValue(final Object value, final Session session)
+    throws RepositoryException {
         Value val;
         ValueFactory fac = session.getValueFactory();
         if(value instanceof Calendar) {
@@ -152,7 +152,7 @@ public class JcrResourceUtil {
             val = fac.createValue(((Number)value).doubleValue());
         } else if (value instanceof Boolean) {
             val = fac.createValue((Boolean) value);
-        } else if ( value instanceof String ){
+        } else if ( value instanceof String ) {
             val = fac.createValue((String)value);
         } else {
             val = null;

Modified: sling/trunk/bundles/jcr/resource/src/main/java/org/apache/sling/jcr/resource/internal/JcrModifiableValueMap.java
URL: http://svn.apache.org/viewvc/sling/trunk/bundles/jcr/resource/src/main/java/org/apache/sling/jcr/resource/internal/JcrModifiableValueMap.java?rev=1652218&r1=1652217&r2=1652218&view=diff
==============================================================================
--- sling/trunk/bundles/jcr/resource/src/main/java/org/apache/sling/jcr/resource/internal/JcrModifiableValueMap.java (original)
+++ sling/trunk/bundles/jcr/resource/src/main/java/org/apache/sling/jcr/resource/internal/JcrModifiableValueMap.java Thu Jan 15 18:46:58 2015
@@ -18,36 +18,25 @@
  */
 package org.apache.sling.jcr.resource.internal;
 
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.Serializable;
-import java.lang.reflect.Array;
-import java.math.BigDecimal;
-import java.util.ArrayList;
 import java.util.Calendar;
 import java.util.Collection;
 import java.util.Collections;
 import java.util.Date;
 import java.util.Iterator;
 import java.util.LinkedHashMap;
-import java.util.List;
 import java.util.Map;
 import java.util.Set;
 
 import javax.jcr.Node;
 import javax.jcr.Property;
 import javax.jcr.PropertyIterator;
-import javax.jcr.PropertyType;
 import javax.jcr.RepositoryException;
 import javax.jcr.Value;
-import javax.jcr.ValueFormatException;
 
 import org.apache.jackrabbit.util.ISO9075;
 import org.apache.jackrabbit.util.Text;
 import org.apache.sling.api.resource.ModifiableValueMap;
 import org.apache.sling.jcr.resource.internal.helper.JcrPropertyMapCacheEntry;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
 
 /**
  * This implementation of the value map allows to change
@@ -58,9 +47,6 @@ import org.slf4j.LoggerFactory;
 public final class JcrModifiableValueMap
     implements ModifiableValueMap {
 
-    /** Default logger */
-    private static Logger LOGGER = LoggerFactory.getLogger(JcrModifiableValueMap.class);
-
     /** The underlying node. */
     private final Node node;
 
@@ -124,7 +110,7 @@ public final class JcrModifiableValueMap
         if ( entry == null ) {
             return null;
         }
-        return convertToType(entry, type);
+        return entry.convertToType(type, node, dynamicClassLoader);
     }
 
     /**
@@ -157,7 +143,7 @@ public final class JcrModifiableValueMap
     public Object get(final Object aKey) {
         final String key = checkKey(aKey.toString());
         final JcrPropertyMapCacheEntry entry = this.read(key);
-        final Object value = (entry == null ? null : entry.getDefaultValueOrNull());
+        final Object value = (entry == null ? null : entry.getPropertyValueOrNull());
         return value;
     }
 
@@ -269,9 +255,9 @@ public final class JcrModifiableValueMap
                 entry = new JcrPropertyMapCacheEntry(prop);
                 cache.put(key, entry);
 
-                final Object defaultValue = entry.getDefaultValue();
+                final Object defaultValue = entry.getPropertyValue();
                 if (defaultValue != null) {
-                    valueCache.put(key, entry.getDefaultValue());
+                    valueCache.put(key, entry.getPropertyValue());
                 }
             }
             return entry;
@@ -417,187 +403,6 @@ public final class JcrModifiableValueMap
 
     // ---------- Implementation helper
 
-    @SuppressWarnings("unchecked")
-    private <T> T convertToType(final JcrPropertyMapCacheEntry entry, Class<T> type) {
-        T result = null;
-
-        try {
-            final boolean array = type.isArray();
-
-            if (entry.isMulti) {
-
-                if (array) {
-
-                    result = (T) convertToArray(entry,
-                        type.getComponentType());
-
-                } else if (entry.values.length > 0) {
-
-                    result = convertToType(entry, -1, entry.values[0], type);
-
-                }
-
-            } else {
-
-                if (array) {
-
-                    result = (T) convertToArray(entry,
-                            type.getComponentType());
-
-                } else {
-
-                    result = convertToType(entry, -1, entry.values[0], type);
-
-                }
-            }
-
-        } catch (ValueFormatException vfe) {
-            LOGGER.info("converToType: Cannot convert value of " + entry.getDefaultValueOrNull()
-                + " to " + type, vfe);
-        } catch (RepositoryException re) {
-            LOGGER.info("converToType: Cannot get value of " + entry.getDefaultValueOrNull(), re);
-        }
-
-        // fall back to nothing
-        return result;
-    }
-
-    private <T> T[] convertToArray(final JcrPropertyMapCacheEntry entry, Class<T> type)
-    throws ValueFormatException, RepositoryException {
-        List<T> values = new ArrayList<T>();
-        for (int i = 0; i < entry.values.length; i++) {
-            T value = convertToType(entry, i, entry.values[i], type);
-            if (value != null) {
-                values.add(value);
-            }
-        }
-
-        @SuppressWarnings("unchecked")
-        T[] result = (T[]) Array.newInstance(type, values.size());
-
-        return values.toArray(result);
-    }
-
-    @SuppressWarnings("unchecked")
-    private <T> T convertToType(final JcrPropertyMapCacheEntry entry,
-                                final int index,
-                                final Value jcrValue,
-                                final Class<T> type)
-    throws ValueFormatException, RepositoryException {
-        Object defaultValue = entry.getDefaultValue();
-        if ( type.isInstance(defaultValue) ) {
-            return (T) defaultValue;
-        }
-
-        if (String.class == type) {
-            return (T) jcrValue.getString();
-
-        } else if (Byte.class == type) {
-            return (T) Byte.valueOf((byte) jcrValue.getLong());
-
-        } else if (Short.class == type) {
-            return (T) Short.valueOf((short) jcrValue.getLong());
-
-        } else if (Integer.class == type) {
-            return (T) Integer.valueOf((int) jcrValue.getLong());
-
-        } else if (Long.class == type) {
-            if (jcrValue.getType() == PropertyType.BINARY) {
-                if (index == -1) {
-                    return (T) Long.valueOf(entry.property.getLength());
-                }
-                return (T) Long.valueOf(entry.property.getLengths()[index]);
-            }
-            return (T) Long.valueOf(jcrValue.getLong());
-
-        } else if (Float.class == type) {
-            return (T) Float.valueOf((float) jcrValue.getDouble());
-
-        } else if (Double.class == type) {
-            return (T) Double.valueOf(jcrValue.getDouble());
-
-        } else if (BigDecimal.class == type) {
-            return (T) jcrValue.getDecimal();
-
-        } else if (Boolean.class == type) {
-            return (T) Boolean.valueOf(jcrValue.getBoolean());
-
-        } else if (Date.class == type) {
-            if ( jcrValue.getType() == PropertyType.BINARY ) {
-                final Object obj = deserializeObject(jcrValue);
-                if ( obj instanceof Date) {
-                    return (T) obj;
-                }
-            } else {
-                return (T) jcrValue.getDate().getTime();
-            }
-
-        } else if (Calendar.class == type) {
-            if ( jcrValue.getType() == PropertyType.BINARY ) {
-                final Object obj = deserializeObject(jcrValue);
-                if ( obj instanceof Date) {
-                    final Calendar c = Calendar.getInstance();
-                    c.setTime((Date)obj);
-                    return (T) c;
-                }
-            } else {
-                return (T) jcrValue.getDate();
-            }
-
-        } else if (Value.class == type) {
-            return (T) jcrValue;
-
-        } else if (Property.class == type) {
-            return (T) entry.property;
-
-        } else if (ObjectInputStream.class == type) {
-            if ( jcrValue.getType() == PropertyType.BINARY ) {
-                try {
-                    return (T) new ObjectInputStream(jcrValue.getBinary().getStream(), this.dynamicClassLoader);
-                } catch (IOException ioe) {
-                    // ignore and use fallback
-                }
-            }
-        } else if (Serializable.class.isAssignableFrom(type)
-                && jcrValue.getType() == PropertyType.BINARY) {
-            final Object obj = deserializeObject(jcrValue);
-            if ( type.isInstance(obj) ) {
-                return (T)obj;
-            }
-        }
-
-        // fallback in case of unsupported type
-        return null;
-    }
-
-    /**
-     * Deserialize a binary object
-     * @param jcrValue The jcr property value
-     * @return The object or {@code null}
-     * @throws RepositoryException
-     */
-    private Object deserializeObject(final Value jcrValue) throws RepositoryException {
-        ObjectInputStream ois = null;
-        try {
-            ois = new ObjectInputStream(jcrValue.getBinary().getStream(), this.dynamicClassLoader);
-            final Object obj = ois.readObject();
-            return obj;
-        } catch (ClassNotFoundException cnfe) {
-             // ignore and use fallback
-        } catch (IOException ioe) {
-            // ignore and use fallback
-        } finally {
-            if ( ois != null ) {
-                try {
-                    ois.close();
-                } catch (IOException ignore) {
-                    // ignore
-                }
-            }
-        }
-        return null;
-    }
-
     private Class<?> normalizeClass(Class<?> type) {
         if (Calendar.class.isAssignableFrom(type)) {
             type = Calendar.class;
@@ -613,38 +418,13 @@ public final class JcrModifiableValueMap
 
     private Map<String, Object> transformEntries(final Map<String, JcrPropertyMapCacheEntry> map) {
 
-        Map<String, Object> transformedEntries = new LinkedHashMap<String, Object>(map.size());
-        for ( Map.Entry<String, JcrPropertyMapCacheEntry> entry : map.entrySet() )
-            transformedEntries.put(entry.getKey(), entry.getValue().getDefaultValueOrNull());
+        final Map<String, Object> transformedEntries = new LinkedHashMap<String, Object>(map.size());
+        for ( final Map.Entry<String, JcrPropertyMapCacheEntry> entry : map.entrySet() )
+            transformedEntries.put(entry.getKey(), entry.getValue().getPropertyValueOrNull());
 
         return transformedEntries;
     }
 
-    /**
-     * This is an extended version of the object input stream which uses the
-     * thread context class loader.
-     */
-    private static class ObjectInputStream extends java.io.ObjectInputStream {
-
-        private ClassLoader classloader;
-
-        public ObjectInputStream(final InputStream in, final ClassLoader classLoader) throws IOException {
-            super(in);
-            this.classloader = classLoader;
-        }
-
-        /**
-         * @see java.io.ObjectInputStream#resolveClass(java.io.ObjectStreamClass)
-         */
-        @Override
-        protected Class<?> resolveClass(java.io.ObjectStreamClass classDesc) throws IOException, ClassNotFoundException {
-            if ( this.classloader != null ) {
-                return this.classloader.loadClass(classDesc.getName());
-            }
-            return super.resolveClass(classDesc);
-        }
-    }
-
     // ---------- Map
 
     /**
@@ -668,19 +448,17 @@ public final class JcrModifiableValueMap
         readFully();
         final Object oldValue = this.get(key);
         try {
-            final JcrPropertyMapCacheEntry entry = new JcrPropertyMapCacheEntry(value, getNode().getSession());
+            final JcrPropertyMapCacheEntry entry = new JcrPropertyMapCacheEntry(value, this.node);
             this.cache.put(key, entry);
             final String name = escapeKeyName(key);
             if ( NodeUtil.MIXIN_TYPES.equals(name) ) {
-                NodeUtil.handleMixinTypes(node, entry.values);
+                NodeUtil.handleMixinTypes(node, entry.convertToType(String[].class, node, dynamicClassLoader));
             } else if ( "jcr:primaryType".equals(name) ) {
-                node.setPrimaryType(entry.values[0].getString());
+                node.setPrimaryType(entry.convertToType(String.class, node, dynamicClassLoader));
+            } else if ( entry.isArray() ) {
+                node.setProperty(name, entry.convertToType(Value[].class, node, dynamicClassLoader));
             } else {
-                if ( entry.isMulti ) {
-                    node.setProperty(name, entry.values);
-                } else {
-                    node.setProperty(name, entry.values[0]);
-                }
+                node.setProperty(name, entry.convertToType(Value.class, node, dynamicClassLoader));
             }
         } catch (final RepositoryException re) {
             throw new IllegalArgumentException("Value for key " + key + " can't be put into node: " + value, re);

Modified: sling/trunk/bundles/jcr/resource/src/main/java/org/apache/sling/jcr/resource/internal/NodeUtil.java
URL: http://svn.apache.org/viewvc/sling/trunk/bundles/jcr/resource/src/main/java/org/apache/sling/jcr/resource/internal/NodeUtil.java?rev=1652218&r1=1652217&r2=1652218&view=diff
==============================================================================
--- sling/trunk/bundles/jcr/resource/src/main/java/org/apache/sling/jcr/resource/internal/NodeUtil.java (original)
+++ sling/trunk/bundles/jcr/resource/src/main/java/org/apache/sling/jcr/resource/internal/NodeUtil.java Thu Jan 15 18:46:58 2015
@@ -23,7 +23,6 @@ import java.util.Set;
 
 import javax.jcr.Node;
 import javax.jcr.RepositoryException;
-import javax.jcr.Value;
 import javax.jcr.nodetype.NodeType;
 
 public abstract class NodeUtil {
@@ -37,12 +36,12 @@ public abstract class NodeUtil {
     /**
      * Update the mixin node types
      */
-    public static void handleMixinTypes(final Node node, final Value[] mixinTypes)
+    public static void handleMixinTypes(final Node node, final String[] mixinTypes)
     throws RepositoryException {
         final Set<String> newTypes = new HashSet<String>();
         if ( mixinTypes != null ) {
-            for(final Value value : mixinTypes ) {
-                newTypes.add(value.getString());
+            for(final String value : mixinTypes ) {
+                newTypes.add(value);
             }
         }
         final Set<String> oldTypes = new HashSet<String>();

Modified: sling/trunk/bundles/jcr/resource/src/main/java/org/apache/sling/jcr/resource/internal/helper/JcrPropertyMapCacheEntry.java
URL: http://svn.apache.org/viewvc/sling/trunk/bundles/jcr/resource/src/main/java/org/apache/sling/jcr/resource/internal/helper/JcrPropertyMapCacheEntry.java?rev=1652218&r1=1652217&r2=1652218&view=diff
==============================================================================
--- sling/trunk/bundles/jcr/resource/src/main/java/org/apache/sling/jcr/resource/internal/helper/JcrPropertyMapCacheEntry.java (original)
+++ sling/trunk/bundles/jcr/resource/src/main/java/org/apache/sling/jcr/resource/internal/helper/JcrPropertyMapCacheEntry.java Thu Jan 15 18:46:58 2015
@@ -19,48 +19,45 @@ package org.apache.sling.jcr.resource.in
 import java.io.ByteArrayInputStream;
 import java.io.ByteArrayOutputStream;
 import java.io.IOException;
+import java.io.InputStream;
+import java.io.ObjectInputStream;
 import java.io.ObjectOutputStream;
+import java.io.ObjectStreamClass;
 import java.io.Serializable;
+import java.lang.reflect.Array;
+import java.math.BigDecimal;
+import java.util.ArrayList;
+import java.util.Calendar;
+import java.util.Date;
+import java.util.List;
 
+import javax.jcr.Node;
 import javax.jcr.Property;
 import javax.jcr.PropertyType;
 import javax.jcr.RepositoryException;
 import javax.jcr.Session;
 import javax.jcr.Value;
+import javax.jcr.ValueFormatException;
 
 import org.apache.commons.lang.ArrayUtils;
+import org.apache.sling.jcr.resource.JcrPropertyMap;
 import org.apache.sling.jcr.resource.JcrResourceUtil;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
 
 public class JcrPropertyMapCacheEntry {
-    public final Property property;
-    public final boolean isMulti;
-    public final Value[] values;
 
-    private final Object defaultValue;
+    /** Global logger */
+    private static Logger LOGGER = LoggerFactory.getLogger(JcrPropertyMap.class);
 
-    /**
-     * Create a value for the object.
-     * If the value type is supported directly through a jcr property type,
-     * the corresponding value is created. If the value is serializable,
-     * it is serialized through an object stream. Otherwise null is returned.
-     */
-    private Value createValue(final Object obj, final Session session)
-    throws RepositoryException {
-        Value value = JcrResourceUtil.createValue(obj, session);
-        if ( value == null && obj instanceof Serializable ) {
-            try {
-                final ByteArrayOutputStream baos = new ByteArrayOutputStream();
-                final ObjectOutputStream oos = new ObjectOutputStream(baos);
-                oos.writeObject(obj);
-                oos.close();
-                final ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());
-                value = session.getValueFactory().createValue(session.getValueFactory().createBinary(bais));
-            } catch (IOException ioe) {
-                // we ignore this here and return null
-            }
-        }
-        return value;
-    }
+    /** The JCR property - only set for existing values. */
+    private final Property property;
+
+    /** Whether this is an array or a single value. */
+    private final boolean isArray;
+
+    /** The value of the object. */
+    private final Object propertyValue;
 
     /**
      * Create a new cache entry from a property.
@@ -68,47 +65,70 @@ public class JcrPropertyMapCacheEntry {
     public JcrPropertyMapCacheEntry(final Property prop)
     throws RepositoryException {
         this.property = prop;
-        if ( prop.isMultiple() ) {
-            isMulti = true;
-            values = prop.getValues();
+        this.isArray = prop.isMultiple();
+        if (property.getType() != PropertyType.BINARY) {
+            this.propertyValue = JcrResourceUtil.toJavaObject(prop);
         } else {
-            isMulti = false;
-            values = new Value[] {prop.getValue()};
-        }
-        if (isDefaultValueCacheable()) {
-            this.defaultValue = JcrResourceUtil.toJavaObject(prop);
-        } else {
-            this.defaultValue = null;
+            this.propertyValue = null;
         }
     }
 
     /**
      * Create a new cache entry from a value.
      */
-    public JcrPropertyMapCacheEntry(final Object value, final Session session)
+    public JcrPropertyMapCacheEntry(final Object value, final Node node)
     throws RepositoryException {
         this.property = null;
-        this.defaultValue = value;
-        if ( value.getClass().isArray() ) {
-            this.isMulti = true;
-            final Object[] values = convertToObject(value);;
-            this.values = new Value[values.length];
+        this.propertyValue = value;
+        this.isArray = value.getClass().isArray();
+        // check if values can be stored in JCR
+        if ( isArray ) {
+            final Object[] values = convertToObjectArray(value);
             for(int i=0; i<values.length; i++) {
-                this.values[i] = this.createValue(values[i], session);
-                if ( this.values[i] == null ) {
+                final Value val = this.createValue(values[i], node);
+                if ( val == null ) {
                     throw new IllegalArgumentException("Value can't be stored in the repository: " + values[i]);
                 }
             }
         } else {
-            this.isMulti = false;
-            this.values = new Value[] {this.createValue(value, session)};
-            if ( this.values[0] == null ) {
+            final Value val = this.createValue(value, node);
+            if ( val == null ) {
                 throw new IllegalArgumentException("Value can't be stored in the repository: " + value);
             }
         }
+     }
+
+    /**
+     * Create a value for the object.
+     * If the value type is supported directly through a jcr property type,
+     * the corresponding value is created. If the value is serializable,
+     * it is serialized through an object stream. Otherwise null is returned.
+     */
+    private Value createValue(final Object obj, final Node node)
+    throws RepositoryException {
+        final Session session = node.getSession();
+        Value value = JcrResourceUtil.createValue(obj, session);
+        if ( value == null && obj instanceof Serializable ) {
+            try {
+                final ByteArrayOutputStream baos = new ByteArrayOutputStream();
+                final ObjectOutputStream oos = new ObjectOutputStream(baos);
+                oos.writeObject(obj);
+                oos.close();
+                final ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());
+                value = session.getValueFactory().createValue(session.getValueFactory().createBinary(bais));
+            } catch (IOException ioe) {
+                // we ignore this here and return null
+            }
+        }
+        return value;
     }
 
-    private Object[] convertToObject(final Object value) {
+    /**
+     * Convert the object to an array
+     * @param value The array
+     * @return an object array
+     */
+    private Object[] convertToObjectArray(final Object value) {
         final Object[] values;
         if (value instanceof long[]) {
             values = ArrayUtils.toObject((long[])value);
@@ -134,19 +154,272 @@ public class JcrPropertyMapCacheEntry {
         return values;
     }
 
-    public Object getDefaultValue() throws RepositoryException {
-        return this.defaultValue != null ? this.defaultValue : JcrResourceUtil.toJavaObject(property);
+    /**
+     * Whether this value is an array or not
+     * @return {@code true} if an array.
+     */
+    public boolean isArray() {
+        return this.isArray;
     }
 
-    public Object getDefaultValueOrNull() {
+    /**
+     * Get the current property value.
+     * @return The current value
+     * @throws RepositoryException If something goes wrong
+     */
+    public Object getPropertyValue() throws RepositoryException {
+        return this.propertyValue != null ? this.propertyValue : JcrResourceUtil.toJavaObject(property);
+    }
+
+    /**
+     * Get the current property value.
+     * @return The current value or {@code null} if not possible.
+     */
+    public Object getPropertyValueOrNull() {
         try {
-            return getDefaultValue();
-        } catch (RepositoryException e) {
+            return getPropertyValue();
+        } catch (final RepositoryException e) {
             return null;
         }
     }
 
-    private boolean isDefaultValueCacheable() throws RepositoryException {
-        return property.getType() != PropertyType.BINARY;
+    /**
+     * Convert the default value to the given type
+     * @param type The type class
+     * @param session The JCR session
+     * @return The converted object
+     */
+    @SuppressWarnings("unchecked")
+    public <T> T convertToType(final Class<T> type,
+            final Node node,
+            final ClassLoader dynamicClassLoader) {
+        T result = null;
+
+        try {
+            final boolean targetIsArray = type.isArray();
+
+            if (this.isArray) {
+
+                final Object[] sourceArray = convertToObjectArray(this.getPropertyValue());
+                if (targetIsArray) {
+                    result = (T) convertToArray(sourceArray, type.getComponentType(), node, dynamicClassLoader);
+                } else if (sourceArray.length > 0) {
+                    result = convertToType(-1, sourceArray[0], type, node, dynamicClassLoader);
+                }
+
+            } else {
+
+                final Object sourceObject = this.getPropertyValue();
+                if (targetIsArray) {
+                    result = (T) convertToArray(new Object[] {sourceObject}, type.getComponentType(), node, dynamicClassLoader);
+                } else {
+                    result = convertToType(-1, sourceObject, type, node, dynamicClassLoader);
+                }
+            }
+
+        } catch (final NumberFormatException vfe) {
+            LOGGER.info("converToType: Cannot convert value of " + this.getPropertyValueOrNull()
+                    + " to " + type, vfe);
+        } catch (final IllegalArgumentException vfe) {
+            LOGGER.info("converToType: Cannot convert value of " + this.getPropertyValueOrNull()
+                    + " to " + type, vfe);
+        } catch (final ValueFormatException vfe) {
+            LOGGER.info("converToType: Cannot convert value of " + this.getPropertyValueOrNull()
+                + " to " + type, vfe);
+        } catch (RepositoryException re) {
+            LOGGER.info("converToType: Cannot get value of " + this.getPropertyValueOrNull(), re);
+        }
+
+        // fall back to nothing
+        return result;
+    }
+
+    private <T> T[] convertToArray(final Object[] sourceArray,
+            final Class<T> type,
+            final Node node,
+            final ClassLoader dynamicClassLoader)
+    throws ValueFormatException, RepositoryException {
+        List<T> values = new ArrayList<T>();
+        for (int i = 0; i < sourceArray.length; i++) {
+            T value = convertToType(i, sourceArray[i], type, node, dynamicClassLoader);
+            if (value != null) {
+                values.add(value);
+            }
+        }
+
+        @SuppressWarnings("unchecked")
+        T[] result = (T[]) Array.newInstance(type, values.size());
+
+        return values.toArray(result);
+    }
+
+    @SuppressWarnings("unchecked")
+    private <T> T convertToType(final int index,
+                                final Object initialValue,
+                                final Class<T> type,
+                                final Node node,
+                                final ClassLoader dynamicClassLoader)
+    throws ValueFormatException, RepositoryException {
+        if ( type.isInstance(initialValue) ) {
+            return (T) initialValue;
+        }
+
+        Object value = initialValue;
+
+        // special case input stream first
+        if ( value instanceof InputStream ) {
+            // object input stream
+            if ( ObjectInputStream.class.isAssignableFrom(type) ) {
+                try {
+                    return (T) new PropertyObjectInputStream((InputStream)value, dynamicClassLoader);
+                } catch (final IOException ioe) {
+                    // ignore and use fallback
+                }
+
+            // any number: length of binary
+            } else if ( Number.class.isAssignableFrom(type) ) {
+                if (index == -1) {
+                    value = Long.valueOf(this.property.getLength());
+                } else {
+                    value = Long.valueOf(this.property.getLengths()[index]);
+                }
+
+            // string: read binary
+            } else if ( String.class == type) {
+                final InputStream in = (InputStream)value;
+                try {
+                    final ByteArrayOutputStream baos = new ByteArrayOutputStream();
+                    final byte[] buffer = new byte[2048];
+                    int l;
+                    while ( (l = in.read(buffer)) >= 0 ) {
+                        if ( l > 0 ) {
+                            baos.write(buffer, 0, l);
+                        }
+                    }
+                    value = new String(baos.toByteArray(), "UTF-8");
+                } catch (final IOException e) {
+                    throw new IllegalArgumentException(e);
+                } finally {
+                    try {
+                        in.close();
+                    } catch (final IOException ignore) {
+                        // ignore
+                    }
+                }
+
+            // any serializable
+            } else if ( Serializable.class.isAssignableFrom(type) ) {
+                ObjectInputStream ois = null;
+                try {
+                    ois = new PropertyObjectInputStream((InputStream)value, dynamicClassLoader);
+                    final Object obj = ois.readObject();
+                    if ( type.isInstance(obj) ) {
+                        return (T)obj;
+                    }
+                    value = obj;
+                } catch (final ClassNotFoundException cnfe) {
+                     // ignore and use fallback
+                } catch (final IOException ioe) {
+                    // ignore and use fallback
+                } finally {
+                    if ( ois != null ) {
+                        try {
+                            ois.close();
+                        } catch (final IOException ignore) {
+                            // ignore
+                        }
+                    }
+                }
+            }
+        }
+
+        if (String.class == type) {
+            return (T) getConverter(value).toString();
+
+        } else if (Byte.class == type) {
+            return (T) getConverter(value).toByte();
+
+        } else if (Short.class == type) {
+            return (T) getConverter(value).toShort();
+
+        } else if (Integer.class == type) {
+            return (T) getConverter(value).toInteger();
+
+        } else if (Long.class == type) {
+            return (T) getConverter(value).toLong();
+
+        } else if (Float.class == type) {
+            return (T) getConverter(value).toFloat();
+
+        } else if (Double.class == type) {
+            return (T) getConverter(value).toDouble();
+
+        } else if (BigDecimal.class == type) {
+            return (T) getConverter(value).toBigDecimal();
+
+        } else if (Boolean.class == type) {
+            return (T) getConverter(value).toBoolean();
+
+        } else if (Date.class == type) {
+            return (T) getConverter(value).toDate();
+
+        } else if (Calendar.class == type) {
+            return (T) getConverter(value).toCalendar();
+
+        } else if (Value.class == type) {
+            return (T) this.createValue(value, node);
+
+        } else if (Property.class == type) {
+            return (T) this.property;
+        }
+
+        // fallback in case of unsupported type
+        return null;
+    }
+
+    /**
+     * Create a converter for an object.
+     * @param value  The object to convert
+     * @return  A converter for {@code value}
+     */
+    private Converter getConverter(final Object value) {
+        if ( value instanceof Number ) {
+            // byte, short, int, long, double, float, BigDecimal
+            return new NumberConverter((Number)value);
+        } else if ( value instanceof Boolean ) {
+            return new BooleanConverter((Boolean)value);
+        } else if ( value instanceof Date ) {
+            return new DateConverter((Date)value);
+        } else if ( value instanceof Calendar ) {
+            return new CalendarConverter((Calendar)value);
+        }
+        // default string based
+        return new StringConverter(value);
+    }
+
+    /**
+     * This is an extended version of the object input stream which uses the
+     * thread context class loader.
+     */
+    private static class PropertyObjectInputStream extends ObjectInputStream {
+
+        private final ClassLoader classloader;
+
+        public PropertyObjectInputStream(final InputStream in, final ClassLoader classLoader) throws IOException {
+            super(in);
+            this.classloader = classLoader;
+        }
+
+        /**
+         * @see java.io.ObjectInputStream#resolveClass(java.io.ObjectStreamClass)
+         */
+        @Override
+        protected Class<?> resolveClass(final ObjectStreamClass classDesc)
+        throws IOException, ClassNotFoundException {
+            if ( this.classloader != null ) {
+                return this.classloader.loadClass(classDesc.getName());
+            }
+            return super.resolveClass(classDesc);
+        }
     }
 }
\ No newline at end of file