This is an automated email from the ASF dual-hosted git repository. rombert pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/sling-org-apache-sling-nosql-generic.git
commit d1d2508be01ee99c78d666c3419c4b96c359859f Author: Stefan Seifert <[email protected]> AuthorDate: Tue Jun 2 16:17:06 2015 +0000 SLING-4381 keep Calendar and byte[] types in ValueMap, and convert it from and back when passing over to NoSql adapter git-svn-id: https://svn.apache.org/repos/asf/sling/trunk@1683132 13f79535-47bb-0310-9956-ffa450edef68 --- .../resource/impl/NoSqlResourceProvider.java | 2 +- .../nosql/generic/resource/impl/NoSqlValueMap.java | 63 ++------ .../impl/ValueMapConvertingNoSqlAdapter.java | 170 +++++++++++++++++++++ .../impl/AbstractNoSqlResourceProviderTest.java | 2 +- 4 files changed, 188 insertions(+), 49 deletions(-) diff --git a/src/main/java/org/apache/sling/nosql/generic/resource/impl/NoSqlResourceProvider.java b/src/main/java/org/apache/sling/nosql/generic/resource/impl/NoSqlResourceProvider.java index acad8b4..056c03e 100644 --- a/src/main/java/org/apache/sling/nosql/generic/resource/impl/NoSqlResourceProvider.java +++ b/src/main/java/org/apache/sling/nosql/generic/resource/impl/NoSqlResourceProvider.java @@ -60,7 +60,7 @@ public class NoSqlResourceProvider implements ResourceProvider, ModifyingResourc private final Set<String> deletedResources = new HashSet<String>(); public NoSqlResourceProvider(NoSqlAdapter adapter, EventAdmin eventAdmin) { - this.adapter = adapter; + this.adapter = new ValueMapConvertingNoSqlAdapter(adapter); this.eventAdmin = eventAdmin; } diff --git a/src/main/java/org/apache/sling/nosql/generic/resource/impl/NoSqlValueMap.java b/src/main/java/org/apache/sling/nosql/generic/resource/impl/NoSqlValueMap.java index 42e07f9..6f7fcf4 100644 --- a/src/main/java/org/apache/sling/nosql/generic/resource/impl/NoSqlValueMap.java +++ b/src/main/java/org/apache/sling/nosql/generic/resource/impl/NoSqlValueMap.java @@ -21,16 +21,10 @@ package org.apache.sling.nosql.generic.resource.impl; import java.io.ByteArrayInputStream; import java.io.IOException; import java.io.InputStream; -import java.text.DateFormat; -import java.text.ParseException; -import java.text.SimpleDateFormat; import java.util.Calendar; import java.util.Date; -import java.util.Locale; import java.util.Map; -import javax.xml.bind.DatatypeConverter; - import org.apache.commons.io.IOUtils; import org.apache.sling.api.resource.ModifiableValueMap; import org.apache.sling.api.resource.Resource; @@ -56,25 +50,10 @@ class NoSqlValueMap extends ValueMapDecorator implements ModifiableValueMap { @Override public <T> T get(String name, Class<T> type) { - if (type == Calendar.class) { - Date date = get(name, Date.class); - if (date != null) { - Calendar calendar = Calendar.getInstance(); - calendar.setTime(date); - return (T)calendar; - } - else { - return null; - } - } - else if (type == Date.class) { - Object value = get(name); - if (value instanceof String) { - try { - return (T)getISO8601Format().parse((String)value); - } catch (ParseException e) { - return null; - } + if (type == Date.class) { + Calendar value = get(name, Calendar.class); + if (value != null) { + return (T)value.getTime(); } } else if (type == InputStream.class) { @@ -87,13 +66,6 @@ class NoSqlValueMap extends ValueMapDecorator implements ModifiableValueMap { return null; } } - else if (type == byte[].class) { - // Support conversion from base64 string to byte array - Object value = get(name); - if (value instanceof String) { - return (T)DatatypeConverter.parseBase64Binary((String)value); - } - } else if ( type == null ) { return (T) super.get(name); } @@ -128,11 +100,10 @@ class NoSqlValueMap extends ValueMapDecorator implements ModifiableValueMap { } private static Object convertForWrite(Object value) { - if (value instanceof Calendar) { - value = getISO8601Format().format(((Calendar)value).getTime()); - } if (value instanceof Date) { - value = getISO8601Format().format((Date)value); + Calendar calendar = Calendar.getInstance(); + calendar.setTime((Date)value); + value = calendar; } else if (value instanceof InputStream) { // Store InputStream values as byte array @@ -142,25 +113,27 @@ class NoSqlValueMap extends ValueMapDecorator implements ModifiableValueMap { throw new RuntimeException("Unable to convert input stream to byte array."); } } - else if (value instanceof byte[]) { - value = DatatypeConverter.printBase64Binary((byte[])value); - } - else if (value != null && !isValidPrimitveType(value.getClass())) { + else if (value != null && !isValidType(value.getClass())) { throw new IllegalArgumentException("Data type not supported for NoSqlValueMap: " + value.getClass()); } return value; } - static boolean isValidPrimitveType(Class clazz) { + static boolean isValidType(Class clazz) { if (clazz.isArray()) { - return isValidPrimitveType(clazz.getComponentType()); + if (clazz.getComponentType() == byte.class) { + // byte only supported as array + return true; + } + return isValidType(clazz.getComponentType()); } else { return clazz == String.class || clazz == Integer.class || clazz == Long.class || clazz == Double.class - || clazz == Boolean.class; + || clazz == Boolean.class + || Calendar.class.isAssignableFrom(clazz); } } @@ -171,8 +144,4 @@ class NoSqlValueMap extends ValueMapDecorator implements ModifiableValueMap { return map; } - private static DateFormat getISO8601Format() { - return new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSZ", Locale.US); - } - } diff --git a/src/main/java/org/apache/sling/nosql/generic/resource/impl/ValueMapConvertingNoSqlAdapter.java b/src/main/java/org/apache/sling/nosql/generic/resource/impl/ValueMapConvertingNoSqlAdapter.java new file mode 100644 index 0000000..62974f0 --- /dev/null +++ b/src/main/java/org/apache/sling/nosql/generic/resource/impl/ValueMapConvertingNoSqlAdapter.java @@ -0,0 +1,170 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.sling.nosql.generic.resource.impl; + +import java.text.DateFormat; +import java.text.ParseException; +import java.text.SimpleDateFormat; +import java.util.Calendar; +import java.util.Date; +import java.util.HashMap; +import java.util.Iterator; +import java.util.Locale; +import java.util.Map; + +import javax.xml.bind.DatatypeConverter; + +import org.apache.sling.nosql.generic.adapter.NoSqlAdapter; +import org.apache.sling.nosql.generic.adapter.NoSqlData; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Special adapter wrapper that converts all Calendar and byte[] values in ValueMap to String values + * when passing to the underlying NoSql adapter and back to typed values when reading from it. + * This is required because too many implementations access ValueMap without type specifier so + * we cannot only rely on the type conversion in the typed get methods of a ValueMap. + */ +class ValueMapConvertingNoSqlAdapter implements NoSqlAdapter { + + private static final String PREFIX_CALENDAR = "{{calendar}}"; + private static final String PREFIX_BYTE_ARRAY = "{{bytes}}"; + + private final NoSqlAdapter delegate; + + private static final Logger log = LoggerFactory.getLogger(ValueMapConvertingNoSqlAdapter.class); + + public ValueMapConvertingNoSqlAdapter(NoSqlAdapter delegate) { + this.delegate = delegate; + } + + public boolean validPath(String path) { + return delegate.validPath(path); + } + + public NoSqlData get(String path) { + return deserializeUnsupportedTypes(delegate.get(path)); + } + + public Iterator<NoSqlData> getChildren(String parentPath) { + return deserializeUnsupportedTypes(delegate.getChildren(parentPath)); + } + + public boolean store(NoSqlData data) { + return delegate.store(serializeUnsupportedTypes(data)); + } + + public boolean deleteRecursive(String path) { + return delegate.deleteRecursive(path); + } + + public Iterator<NoSqlData> query(String query, String language) { + return deserializeUnsupportedTypes(delegate.query(query, language)); + } + + private Iterator<NoSqlData> deserializeUnsupportedTypes(final Iterator<NoSqlData> source) { + if (source == null) { + return null; + } + return new Iterator<NoSqlData>() { + @Override + public boolean hasNext() { + return source.hasNext(); + } + @Override + public NoSqlData next() { + return deserializeUnsupportedTypes(source.next()); + } + @Override + public void remove() { + source.remove(); + } + }; + } + + private NoSqlData serializeUnsupportedTypes(NoSqlData data) { + if (data == null) { + return null; + } + + Map<String,Object> serializedMap = new HashMap<String, Object>(); + + for (Map.Entry<String, Object> entry : data.getProperties().entrySet()) { + Object serializedValue = entry.getValue(); + + // Calendar.class + if (entry.getValue() instanceof Calendar) { + serializedValue = PREFIX_CALENDAR + getISO8601Format().format(((Calendar)entry.getValue()).getTime()); + } + + // byte[].class + else if (entry.getValue() instanceof byte[]) { + serializedValue = PREFIX_BYTE_ARRAY + DatatypeConverter.printBase64Binary((byte[])entry.getValue()); + } + + serializedMap.put(entry.getKey(), serializedValue); + } + + return new NoSqlData(data.getPath(), serializedMap); + } + + private NoSqlData deserializeUnsupportedTypes(NoSqlData data) { + if (data == null) { + return null; + } + + Map<String,Object> deserializedMap = new HashMap<String, Object>(); + + for (Map.Entry<String, Object> entry : data.getProperties().entrySet()) { + Object deserializedValue = entry.getValue(); + if (entry.getValue() instanceof String) { + String value = (String)entry.getValue(); + + // Calendar.class + if (value.indexOf(PREFIX_CALENDAR) == 0) { + String calendarValue = value.substring(PREFIX_CALENDAR.length()); + try { + Date date = getISO8601Format().parse((String)calendarValue); + Calendar calendar = Calendar.getInstance(); + calendar.setTime(date); + deserializedValue = calendar; + } + catch (ParseException ex) { + log.warn("Unable to parse serialized calendar value: " + entry.getValue(), ex); + } + } + + // byte[].class + else if (value.indexOf(PREFIX_BYTE_ARRAY) == 0) { + String byteArrayValue = value.substring(PREFIX_BYTE_ARRAY.length()); + deserializedValue = DatatypeConverter.parseBase64Binary(byteArrayValue); + } + + } + deserializedMap.put(entry.getKey(), deserializedValue); + } + + return new NoSqlData(data.getPath(), deserializedMap); + } + + private static DateFormat getISO8601Format() { + return new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSZ", Locale.US); + } + +} diff --git a/src/test/java/org/apache/sling/nosql/generic/resource/impl/AbstractNoSqlResourceProviderTest.java b/src/test/java/org/apache/sling/nosql/generic/resource/impl/AbstractNoSqlResourceProviderTest.java index fde5152..fa3a82e 100644 --- a/src/test/java/org/apache/sling/nosql/generic/resource/impl/AbstractNoSqlResourceProviderTest.java +++ b/src/test/java/org/apache/sling/nosql/generic/resource/impl/AbstractNoSqlResourceProviderTest.java @@ -198,7 +198,7 @@ public abstract class AbstractNoSqlResourceProviderTest { // ensure that value map has only supported primitive types (all other supported types converted to string) ValueMap valueMap = resource1.getValueMap(); for (Map.Entry<String, Object> entry : valueMap.entrySet()) { - assertTrue(NoSqlValueMap.isValidPrimitveType(entry.getValue().getClass())); + assertTrue(NoSqlValueMap.isValidType(entry.getValue().getClass())); } } -- To stop receiving notification emails like this one, please contact "[email protected]" <[email protected]>.
