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