blob: 37e3c45668b88a676f2e5f680ce700b2b6583dcd [file] [log] [blame]
/*
* 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.api.resource.LoginException;
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);
}
@Override
public void checkConnection() throws LoginException {
delegate.checkConnection();
}
@Override
public void createIndexDefinitions() {
delegate.createIndexDefinitions();
}
private static DateFormat getISO8601Format() {
return new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSZ", Locale.US);
}
}