All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.elasticsearch.hadoop.serialization.json.BackportedObjectReader Maven / Gradle / Ivy

The newest version!
/*
 * Licensed to Elasticsearch under one or more contributor
 * license agreements. See the NOTICE file distributed with
 * this work for additional information regarding copyright
 * ownership. Elasticsearch 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.elasticsearch.hadoop.serialization.json;

import java.io.IOException;
import java.lang.reflect.Field;
import java.util.concurrent.ConcurrentHashMap;

import org.codehaus.jackson.JsonFactory;
import org.codehaus.jackson.JsonGenerator;
import org.codehaus.jackson.JsonParser;
import org.codehaus.jackson.JsonProcessingException;
import org.codehaus.jackson.map.DeserializationConfig;
import org.codehaus.jackson.map.DeserializationContext;
import org.codehaus.jackson.map.DeserializerProvider;
import org.codehaus.jackson.map.JsonDeserializer;
import org.codehaus.jackson.map.JsonMappingException;
import org.codehaus.jackson.map.ObjectMapper;
import org.codehaus.jackson.map.deser.StdDeserializationContext;
import org.codehaus.jackson.map.introspect.VisibilityChecker;
import org.codehaus.jackson.map.jsontype.TypeResolverBuilder;
import org.codehaus.jackson.map.type.TypeFactory;
import org.codehaus.jackson.type.JavaType;
import org.elasticsearch.hadoop.util.Assert;
import org.elasticsearch.hadoop.util.ReflectionUtils;

/**
 * Backported class from Jackson 1.8.8 for Jackson 1.5.2.
 * Used only when dealing with Jackson 1.5 otherwise the proper Jackson class is used which
 * saves us the hassle of keeping up with the breaking changes in Jackson library.
 *
 * Builder object that can be used for per-serialization configuration of
 * deserialization parameters, such as root type to use or object
 * to update (instead of constructing new instance).
 * Uses "fluid" (aka builder) pattern so that instances are immutable
 * (and thus fully thread-safe with no external synchronization);
 * new instances are constructed for different configurations.
 * Instances are initially constructed by {@link ObjectMapper} and can be
 * reused.
 *
 * @author tatu
 * @since 1.6
 */

public class BackportedObjectReader implements ObjectReader {

    final static Field ROOT_DESERIALIZERS;

    static {
        Field fl = ReflectionUtils.findField(ObjectMapper.class, "_rootDeserializers");
        Assert.notNull(fl, "Cannot find root deserializers");
        ROOT_DESERIALIZERS = fl;
        ReflectionUtils.makeAccessible(fl);
    }

    /*
    /**********************************************************
    /* Immutable configuration from ObjectMapper
    /**********************************************************
     */

    /**
     * Root-level cached deserializers
     */
    final protected ConcurrentHashMap> _rootDeserializers;

    /**
     * General serialization configuration settings
     */
    protected final DeserializationConfig _config;

    protected final DeserializerProvider _provider;

    /**
     * Factory used for constructing {@link JsonGenerator}s
     */
    protected final JsonFactory _jsonFactory;

    // Support for polymorphic types:
    protected TypeResolverBuilder _defaultTyper;

    // Configurable visibility limits
    protected VisibilityChecker _visibilityChecker;

    /*
    /**********************************************************
    /* Configuration that can be changed during building
    /**********************************************************
     */

    /**
     * Declared type of value to instantiate during deserialization.
     * Defines which deserializer to use; as well as base type of instance
     * to construct if an updatable value is not configured to be used
     * (subject to changes by embedded type information, for polymorphic
     * types). If {@link #_valueToUpdate} is non-null, only used for
     * locating deserializer.
     */
    protected final JavaType _valueType;

    /**
     * Instance to update with data binding; if any. If null,
     * a new instance is created, if non-null, properties of
     * this value object will be updated instead.
     * Note that value can be of almost any type, except not
     * {@link org.codehaus.jackson.map.type.ArrayType}; array
     * types can not be modified because array size is immutable.
     */
    protected final Object _valueToUpdate;


    public static BackportedObjectReader create(ObjectMapper mapper, Class type) {
        return new BackportedObjectReader(mapper, TypeFactory.type(type), null);
    }

    /**
     * Constructor used by {@link ObjectMapper} for initial instantiation
     */
    protected BackportedObjectReader(ObjectMapper mapper, JavaType valueType, Object valueToUpdate) {
        _rootDeserializers = ReflectionUtils.getField(ROOT_DESERIALIZERS, mapper);
        _provider = mapper.getDeserializerProvider();
        _jsonFactory = mapper.getJsonFactory();

        // must make a copy at this point, to prevent further changes from trickling down
        _config = mapper.copyDeserializationConfig();

        _valueType = valueType;
        _valueToUpdate = valueToUpdate;
        if (valueToUpdate != null && valueType.isArrayType()) {
            throw new IllegalArgumentException("Can not update an array value");
        }
    }


    public  BackportedJacksonMappingIterator readValues(JsonParser jp) throws IOException,
            JsonProcessingException {
        DeserializationContext ctxt = _createDeserializationContext(jp, _config);
        return new BackportedJacksonMappingIterator(_valueType, jp, ctxt, _findRootDeserializer(_config, _valueType));
    }


    /**
     * Method called to locate deserializer for the passed root-level value.
     */
    protected JsonDeserializer _findRootDeserializer(DeserializationConfig cfg, JavaType valueType)
            throws JsonMappingException {

        // Sanity check: must have actual type...
        if (valueType == null) {
            throw new JsonMappingException("No value type configured for ObjectReader");
        }

        // First: have we already seen it?
        JsonDeserializer deser = _rootDeserializers.get(valueType);
        if (deser != null) {
            return deser;
        }

        // es-hadoop: findType with 2 args have been removed since 1.9 so this code compiles on 1.8 (which has the fallback method)
        // es-hadoop: on 1.5 only the 2 args method exists, since 1.9 only the one with 3 args hence the if

        // Nope: need to ask provider to resolve it
        deser = _provider.findTypedValueDeserializer(cfg, valueType);
        if (deser == null) { // can this happen?
            throw new JsonMappingException("Can not find a deserializer for type " + valueType);
        }
        _rootDeserializers.put(valueType, deser);
        return deser;
    }

    protected DeserializationContext _createDeserializationContext(JsonParser jp, DeserializationConfig cfg) {
        // 04-Jan-2010, tatu: we do actually need the provider too... (for polymorphic deser)
        return new StdDeserializationContext(cfg, jp, _provider);
    }
}