org.openengsb.core.util.JsonUtils.java Source code

Java tutorial

Introduction

Here is the source code for org.openengsb.core.util.JsonUtils.java

Source

/**
 * Licensed to the Austrian Association for Software Tool Integration (AASTI)
 * under one or more contributor license agreements. See the NOTICE file
 * distributed with this work for additional information regarding copyright
 * ownership. The AASTI 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.openengsb.core.util;

import java.io.IOException;
import java.lang.reflect.Array;
import java.lang.reflect.Constructor;
import java.util.List;

import org.apache.commons.lang.reflect.MethodUtils;
import org.openengsb.core.api.model.OpenEngSBModelEntry;
import org.openengsb.core.api.model.annotation.Model;
import org.openengsb.core.api.remote.MethodCall;
import org.openengsb.core.api.remote.MethodCallMessage;
import org.openengsb.core.api.remote.MethodResult;
import org.openengsb.core.api.remote.MethodResultMessage;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.util.ClassUtils;

import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.core.JsonToken;
import com.fasterxml.jackson.databind.AnnotationIntrospector;
import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.deser.std.StdScalarDeserializer;
import com.fasterxml.jackson.databind.introspect.AnnotationIntrospectorPair;
import com.fasterxml.jackson.databind.introspect.JacksonAnnotationIntrospector;
import com.fasterxml.jackson.databind.module.SimpleModule;
import com.fasterxml.jackson.module.jaxb.JaxbAnnotationIntrospector;
import com.google.common.base.Preconditions;

public final class JsonUtils {

    private static final Logger LOGGER = LoggerFactory.getLogger(JsonUtils.class);

    private static final ObjectMapper MAPPER = new ObjectMapper();
    private static final ObjectMapper MODEL_MAPPER = new ObjectMapper();

    static {
        // adding the additional deserializer needed to deserialize models
        MODEL_MAPPER.registerModule(
                new SimpleModule().addDeserializer(Object.class, new OpenEngSBModelEntryDeserializer()));
    }

    /**
     * Converts an object in JSON format to the given class. Throws an IOException if the conversion could not be
     * performed.
     */
    public static <T> T convertObject(String json, Class<T> clazz) throws IOException {
        try {
            if (clazz.isAnnotationPresent(Model.class)) {
                return MODEL_MAPPER.readValue(json, clazz);
            }
            return MAPPER.readValue(json, clazz);
        } catch (IOException e) {
            String error = String.format("Unable to parse given json '%s' into class '%s'.", json, clazz.getName());
            LOGGER.error(error, e);
            throw new IOException(error, e);
        }
    }

    private static Object convertArgument(String className, Object arg) {
        try {
            Class<?> type = findType(className);
            if (type.isAnnotationPresent(Model.class)) {
                return MODEL_MAPPER.convertValue(arg, type);
            }
            return MAPPER.convertValue(arg, type);
        } catch (ClassNotFoundException e) {
            LOGGER.error("could not convert argument " + arg, e);
            return arg;
        }
    }

    private static Class<?> findType(String className) throws ClassNotFoundException {
        if (className.startsWith("[L")) {
            Class<?> componentType = findType(className.substring(2, className.length() - 1));
            return Array.newInstance(componentType, 0).getClass();
        }
        return JsonUtils.class.getClassLoader().loadClass(className);
    }

    public static void convertAllArgs(MethodCall call) {
        Object[] args = call.getArgs();
        List<String> classes = call.getClasses();
        Preconditions.checkArgument(args.length == classes.size());
        for (int i = 0; i < args.length; i++) {
            args[i] = convertArgument(classes.get(i), args[i]);
        }
    }

    public static void convertResult(MethodResult result) {
        Object convertArgument = convertArgument(result.getClassName(), result.getArg());
        result.setArg(convertArgument);
    }

    public static void convertAllArgs(MethodCallMessage request) {
        convertAllArgs(request.getMethodCall());
    }

    public static void convertResult(MethodResultMessage message) {
        convertResult(message.getResult());
    }

    public static ObjectMapper createObjectMapperWithIntroSpectors() {
        ObjectMapper mapper = new ObjectMapper();
        AnnotationIntrospector primaryIntrospector = new JacksonAnnotationIntrospector();
        AnnotationIntrospector secondaryIntrospector = new JaxbAnnotationIntrospector(mapper.getTypeFactory());
        AnnotationIntrospector introspector = new AnnotationIntrospectorPair(primaryIntrospector,
                secondaryIntrospector);
        mapper.getDeserializationConfig().withAppendedAnnotationIntrospector(introspector);
        mapper.getSerializationConfig().withAppendedAnnotationIntrospector(introspector);
        return mapper;
    }

    private JsonUtils() {
    }

    /**
     * The OpenEngSBModelEntryDeserializer class is needed in order to be able to transform the list of
     * OpenEngSBModelEntry elements, which is contained in every model tail, from a JSON string into a list of actual
     * elements.
     */
    @SuppressWarnings("serial")
    private static class OpenEngSBModelEntryDeserializer extends StdScalarDeserializer<OpenEngSBModelEntry> {
        public OpenEngSBModelEntryDeserializer() {
            super(OpenEngSBModelEntry.class);
        }

        @Override
        public OpenEngSBModelEntry deserialize(JsonParser jp, DeserializationContext ctxt) throws IOException {
            JsonToken token = jp.getCurrentToken();
            OpenEngSBModelEntry entry = new OpenEngSBModelEntry();
            if (token != JsonToken.START_OBJECT) {
                return null;
            } else {
                // skip the JsonToken.START_OBJECT token
                token = jp.nextValue();
            }
            do {
                if (token == JsonToken.END_OBJECT) {
                    Object value = createValueOfEntry(entry);
                    if (value != null) {
                        entry.setValue(value);
                    }
                    return entry;
                } else {
                    if (jp.getCurrentName().equals("key")) {
                        entry.setKey(jp.getValueAsString());
                    } else if (jp.getCurrentName().equals("value")) {
                        entry.setValue(jp.getValueAsString());
                    } else if (jp.getCurrentName().equals("type")) {
                        try {
                            entry.setType(findType(jp.getValueAsString()));
                        } catch (ClassNotFoundException e) {
                            LOGGER.error("Did not find class of type " + jp.getValueAsString(), e);
                            break;
                        }
                    }
                }
                token = jp.nextValue();
            } while (token != null);
            return null;
        }

        /**
         * Converts the string located in the value property of the entry in the correct data format and returns it.
         */
        private Object createValueOfEntry(OpenEngSBModelEntry entry) {
            if (entry.getType().equals(String.class)) {
                return entry.getValue();
            }
            Object element = null;
            if (entry.getType() == null) {
                LOGGER.error("Unknown type for model entry with key {}", entry.getKey());
                return element;
            }
            try {
                Class<?> clazz = entry.getType();
                Constructor<?> constr = ClassUtils.getConstructorIfAvailable(clazz, String.class);
                if (constr != null) {
                    element = constr.newInstance(entry.getValue());
                } else {
                    element = MethodUtils.invokeStaticMethod(clazz, "valueOf", entry.getValue());
                }
            } catch (Exception e) {
                LOGGER.error("Unable to convert value with the key {} to correct type", entry.getKey());
            }
            return element;
        }
    }
}