com.basho.riak.client.convert.JSONConverter.java Source code

Java tutorial

Introduction

Here is the source code for com.basho.riak.client.convert.JSONConverter.java

Source

/*
 * This file is provided 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 com.basho.riak.client.convert;

import static com.basho.riak.client.convert.KeyUtil.getKey;
import static com.basho.riak.client.util.CharsetUtils.asString;
import static com.basho.riak.client.util.CharsetUtils.getCharset;

import java.io.IOException;
import java.util.Collection;
import java.util.Map;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.datatype.joda.JodaModule;

import com.basho.riak.client.IRiakObject;
import com.basho.riak.client.RiakLink;
import com.basho.riak.client.builders.RiakObjectBuilder;
import com.basho.riak.client.cap.VClock;
import com.basho.riak.client.http.util.Constants;
import com.basho.riak.client.query.indexes.RiakIndexes;
import java.util.logging.Level;
import java.util.logging.Logger;

/**
 * Converts a RiakObject's value to an instance of T. T must have a field
 * annotated with {@link RiakKey} or you must construct the converter with a key to use. RiakObject's value *must* be a JSON string.
 * 
 * <p>
 * At present user meta data and {@link RiakLink}s are not converted. This means
 * they are essentially lost in translation.
 * </p>
 * 
 * @author russell
 * 
 */
public class JSONConverter<T> implements Converter<T> {

    private final ObjectMapper objectMapper = new ObjectMapper();
    private final Class<T> clazz;
    private final String bucket;
    private final UsermetaConverter<T> usermetaConverter;
    private final RiakIndexConverter<T> riakIndexConverter;
    private final RiakLinksConverter<T> riakLinksConverter;
    private String defaultKey;

    /**
     * Create a JSONConverter for creating instances of <code>clazz</code> from
     * JSON and instances of {@link IRiakObject} with a JSON payload from
     * instances of <code>clazz</code>
     * 
     * @param clazz the type to convert to/from
     * @param b the bucket
     */
    public JSONConverter(Class<T> clazz, String bucket) {
        this(clazz, bucket, null);
    }

    /**
     * Create a JSONConverter for creating instances of <code>clazz</code> from
     * JSON and instances of {@link IRiakObject} with a JSON payload from
     * instances of <code>clazz</code>
     * 
     * @param clazz the type to convert to/from
     * @param bucket the bucket
     * @param defaultKey
     *            for cases where <code>clazz</code> does not have a
     *            {@link RiakKey} annotated field, pass the key to use in this
     *            conversion.
     */
    public JSONConverter(Class<T> clazz, String bucket, String defaultKey) {
        this.clazz = clazz;
        this.bucket = bucket;
        this.defaultKey = defaultKey;
        this.usermetaConverter = new UsermetaConverter<T>();
        this.riakIndexConverter = new RiakIndexConverter<T>();
        this.riakLinksConverter = new RiakLinksConverter<T>();
        objectMapper.registerModule(new RiakJacksonModule());
        objectMapper.registerModule(new JodaModule());
    }

    /**
     * Converts <code>domainObject</code> to a JSON string and sets that as the
     * payload of a {@link IRiakObject}. Also set the <code>content-type</code>
     * to <code>application/json;charset=UTF-8</code>
     * 
     * @param domainObject
     *            to be converted
     * @param vclock
     *            the vector clock from Riak
     */
    public IRiakObject fromDomain(T domainObject, VClock vclock) throws ConversionException {
        try {
            String key = getKey(domainObject, this.defaultKey);

            if (key == null) {
                throw new NoKeySpecifedException(domainObject);
            }

            final byte[] value = objectMapper.writeValueAsBytes(domainObject);
            Map<String, String> usermetaData = usermetaConverter.getUsermetaData(domainObject);
            RiakIndexes indexes = riakIndexConverter.getIndexes(domainObject);
            Collection<RiakLink> links = riakLinksConverter.getLinks(domainObject);
            return RiakObjectBuilder.newBuilder(bucket, key).withValue(value).withVClock(vclock)
                    .withUsermeta(usermetaData).withIndexes(indexes).withLinks(links)
                    .withContentType(Constants.CTYPE_JSON_UTF8).build();
        } catch (JsonProcessingException e) {
            throw new ConversionException(e);
        } catch (IOException e) {
            throw new ConversionException(e);
        }

    }

    /**
     * Converts the <code>value</code> of <code>riakObject</code> to an instance
     * of <code>T</code>.
     * <p>
     * Beware: at present links and user meta are not converted at present: this is on the way.
     * </p>
     * @param riakObject
     *            the {@link IRiakObject} to convert to instance of
     *            <code>T</code>. NOTE: <code>riakObject.getValue()</code> must be a
     *            JSON string. The charset from
     *            <code>riakObject.getContentType()</code> is used.
     */
    public T toDomain(IRiakObject riakObject) throws ConversionException {
        if (riakObject == null) {
            return null;
        } else if (riakObject.isDeleted()) {
            try {
                T domainObject = clazz.newInstance();
                TombstoneUtil.setTombstone(domainObject, true);
                return domainObject;
            } catch (InstantiationException ex) {
                throw new ConversionException("POJO does not provide no-arg constructor", ex);
            } catch (IllegalAccessException ex) {
                throw new ConversionException(ex);
            }

        } else {

            String json = asString(riakObject.getValue(), getCharset(riakObject.getContentType()));

            try {
                T domainObject = objectMapper.readValue(json, clazz);
                KeyUtil.setKey(domainObject, riakObject.getKey());
                VClockUtil.setVClock(domainObject, riakObject.getVClock());
                usermetaConverter.populateUsermeta(riakObject.getMeta(), domainObject);
                riakIndexConverter.populateIndexes(
                        new RiakIndexes(riakObject.allBinIndexes(), riakObject.allIntIndexesV2()), domainObject);
                riakLinksConverter.populateLinks(riakObject.getLinks(), domainObject);
                return domainObject;
            } catch (JsonProcessingException e) {
                throw new ConversionException(e);
            } catch (IOException e) {
                throw new ConversionException(e);
            }
        }
    }

    /**
     * Returns the {@link ObjectMapper} being used.
     * This is a convenience method to allow changing its behavior.
     * @return The Jackson ObjectMapper
     */
    public ObjectMapper getObjectMapper() {
        return objectMapper;
    }
}