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

de.bild.codec.PojoCodecProvider Maven / Gradle / Ivy

Go to download

A very fast POJO codec for MongoDB (used in conjunction with the Mongo Java Driver) that handles generic types as well as polymorphic class hierarchies

There is a newer version: 2.8.2
Show newest version
package de.bild.codec;


import com.google.common.reflect.AbstractInvocationHandler;
import org.bson.BsonReader;
import org.bson.BsonValue;
import org.bson.BsonWriter;
import org.bson.codecs.Codec;
import org.bson.codecs.CollectibleCodec;
import org.bson.codecs.DecoderContext;
import org.bson.codecs.EncoderContext;
import org.bson.codecs.configuration.CodecProvider;
import org.bson.codecs.configuration.CodecRegistry;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.*;

/**
 * Provides a codec for Pojos
 * Use the internal builder to register classes and packages that can be handled by the codec
 */
public class PojoCodecProvider implements CodecProvider {
    private static final Logger LOGGER = LoggerFactory.getLogger(PojoCodecProvider.class);
    private final TypesModel typesModel;
    private final PojoContext pojoContext;

    PojoCodecProvider(final Set> classes, final Set packages, final List codecResolvers) {
        this.typesModel = new TypesModel(classes, packages);
        this.pojoContext = new PojoContext(typesModel, codecResolvers);
    }

    public static Builder builder() {
        return new Builder();
    }

    @Override
    public  Codec get(Class clazz, CodecRegistry registry) {
        // if clazz has type parameters, we warn the user that generic class definitions are problematic
        Codec codec = pojoContext.get(clazz, registry);
        if (codec instanceof TypeCodec) {
            if (clazz != null && clazz.getTypeParameters().length > 0) {
                LOGGER.warn("Generic classes will only be encoded/decoded with their upper bounds! " +
                        "We could prohibit handling of the pojo codec for those generic classes, " +
                        "but then user would loose flexibility when subclassing such classes. Class: " + clazz.toGenericString());
            }
            TypeCodec typeCodec = (TypeCodec) codec;
            // generate dynamic proxy to add CollectibleCodec functionality
            if (typeCodec.isCollectible()) {
                LOGGER.debug("Enhancing {} to be collectible codec.", typeCodec);
                Class[] proxyInterfaces = new Class[]{CollectibleCodec.class};
                CollectibleCodec collectibleCodec = (CollectibleCodec) Proxy.newProxyInstance(
                        PojoCodecProvider.class.getClassLoader(),
                        proxyInterfaces,
                        new CollectibleCodecDelegator(typeCodec));

                return collectibleCodec;
            }
        }
        return codec;
    }

    /**
     * delegator for CollectibleCodec
     */
    private static class CollectibleCodecDelegator extends AbstractInvocationHandler implements CollectibleCodec {
        private final TypeCodec delegatingCodec;

        public CollectibleCodecDelegator(TypeCodec delegatingCodec) {
            this.delegatingCodec = delegatingCodec;
        }


        @Override
        protected Object handleInvocation(Object proxy, Method method, Object[] args) throws Throwable {
            try {
                return method.invoke(this, args);
            } catch (IllegalAccessException | InvocationTargetException e) {
                LOGGER.warn("An exception was caught while invoking the delegate {} with args {}", method, args);
                LOGGER.debug("Original exception when invoking target.", e);
                // rethrowing cause instead of invocationexception
                throw e.getCause();
            }
        }

        @Override
        public T generateIdIfAbsentFromDocument(T document) {
            return delegatingCodec.generateIdIfAbsentFromDocument(document);
        }

        @Override
        public boolean documentHasId(T document) {
            return delegatingCodec.documentHasId(document);
        }

        @Override
        public BsonValue getDocumentId(T document) {
            return delegatingCodec.getDocumentId(document);
        }

        @Override
        public T decode(BsonReader reader, DecoderContext decoderContext) {
            return delegatingCodec.decode(reader, decoderContext);
        }

        @Override
        public void encode(BsonWriter writer, T value, EncoderContext encoderContext) {
            delegatingCodec.encode(writer, value, encoderContext);
        }

        @Override
        public Class getEncoderClass() {
            return delegatingCodec.getEncoderClass();
        }

        @Override
        public boolean equals(Object obj) {
            return super.equals(obj);
        }

        @Override
        public int hashCode() {
            return super.hashCode();
        }
    }

    /**
     *
     */
    public static class Builder {
        private Set packages = new HashSet<>();
        private Set> classes = new HashSet<>();
        private List codecResolvers = new ArrayList<>();

        private Builder() {
        }

        public Builder setPackages(Set packages) {
            this.packages = packages;
            return this;
        }

        public Builder register(String... packages) {
            this.packages.addAll(Arrays.asList(packages));
            return this;
        }

        public Builder register(Class... classes) {
            this.classes.addAll(Arrays.asList(classes));
            return this;
        }

        /**
         * A CodecResolver is supposed to provide specialized codecs in case the default implementation
         * {@link BasicReflectionCodec} is not sufficient
         *
         * @param codecResolvers a list of CodecResolvers to be registered
         * @return the builder
         */
        public Builder registerCodecResolver(CodecResolver... codecResolvers) {
            this.codecResolvers.addAll(Arrays.asList(codecResolvers));
            return this;
        }

        public PojoCodecProvider build() {
            return new PojoCodecProvider(classes, packages, codecResolvers);
        }
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy