org.springframework.jdbc.repo.impl.RawPropertiesPolymorphicIdentifiedEntityRepoImpl.java Source code

Java tutorial

Introduction

Here is the source code for org.springframework.jdbc.repo.impl.RawPropertiesPolymorphicIdentifiedEntityRepoImpl.java

Source

/* Copyright 2013 Lyor Goldstein
 *
 * Licensed 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.springframework.jdbc.repo.impl;

import java.io.Serializable;
import java.lang.reflect.Method;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;

import org.apache.commons.beanutils.ExtendedBeanUtils;
import org.apache.commons.collections15.Closure;
import org.apache.commons.collections15.ExtendedMapUtils;
import org.apache.commons.collections15.Predicate;
import org.apache.commons.collections15.Transformer;
import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.Validate;
import org.apache.commons.lang3.reflect.ExtendedConstructorUtils;
import org.apache.commons.lang3.tuple.Pair;
import org.springframework.jdbc.repo.IdentifiedEntityIdGenerator;
import org.springframework.jdbc.repo.MutableIdentity;
import org.springframework.jdbc.repo.RawPropertiesRepo;
import org.springframework.util.Assert;

/**
 * @author Lyor Goldstein
 * @since Sep 11, 2013 12:30:07 PM
 */
public class RawPropertiesPolymorphicIdentifiedEntityRepoImpl<E extends MutableIdentity>
        extends AbstractIdentifiedEntityRepo<E> {
    // NOTE: name is specifically selected so as to match "Object#getClass" which we know has no setter
    public static final String CLASS_PROP_NAME = "class";

    protected final RawPropertiesRepo<E> repo;
    protected final Map<Class<?>, Pair<Transformer<Map<String, ?>, E>, Transformer<E, ? extends Map<String, ?>>>> transformersMap = Collections
            .synchronizedMap(
                    new HashMap<Class<?>, Pair<Transformer<Map<String, ?>, E>, Transformer<E, ? extends Map<String, ?>>>>());
    protected final Map<Class<?>, IdentifiedEntityIdGenerator<?>> idGeneratorsMap = Collections
            .synchronizedMap(new HashMap<Class<?>, IdentifiedEntityIdGenerator<?>>());

    public RawPropertiesPolymorphicIdentifiedEntityRepoImpl(RawPropertiesRepo<E> rawRepo) {
        super((rawRepo == null) ? null : rawRepo.getEntityClass(), (rawRepo == null) ? null : rawRepo.idsBuilder());
        repo = Validate.notNull(rawRepo, "No raw repository provided", ArrayUtils.EMPTY_OBJECT_ARRAY);
    }

    @Override
    public List<String> listEntitiesIdentifiers() {
        return repo.listEntities();
    }

    @Override
    public E findEntityById(String id) {
        Map<String, ?> props = repo.getProperties(id);
        if (ExtendedMapUtils.isEmpty(props)) {
            return null;
        }

        Object clazzType = props.get(CLASS_PROP_NAME);
        if (!(clazzType instanceof Class)) {
            throw new IllegalStateException("findEntityById(" + id + ") no effective class");
        }

        Transformer<Map<String, ?>, E> transformer = getPropertiesTransformer((Class<?>) clazzType);
        if (transformer == null) {
            throw new IllegalStateException("findEntityById(" + id + ") no properties transformer");
        }

        return transformer.transform(props);
    }

    @Override
    public boolean entityExists(String id) {
        return repo.entityExists(id);
    }

    @Override
    public List<String> findEntitiesIdentifiers(String propName, Predicate<? super Serializable> predicate) {
        return repo.findEntities(propName, predicate);
    }

    @Override
    public String createEntity(E entity) {
        Assert.notNull(entity, "No entity");

        final IdentifiedEntityIdGenerator<?> entityIdGenerator = getEntityIdGenerator(entity);
        if (entityIdGenerator == null) {
            throw new IllegalStateException(
                    "createEntity(" + entity.getClass().getSimpleName() + ") no identity generator");
        }

        final String prevId = entity.getId();
        final String assignedId = entityIdGenerator.build();
        if (!StringUtils.isEmpty(prevId)) {
            logger.warn("createEntity(" + prevId + ") overwrite entity ID: " + assignedId);
        }
        entity.setId(assignedId);

        Transformer<E, ? extends Map<String, ?>> transformer = getEntityTransformer(entity);
        if (transformer == null) {
            throw new IllegalStateException("createEntity(" + entity + ") no properties transformer");
        }

        Map<String, ?> props = repo.setProperties(entity, transformer);
        if (logger.isTraceEnabled()) {
            ExtendedMapUtils.forAllEntriesDo(props, new Closure<Map.Entry<String, ?>>() {
                @Override
                @SuppressWarnings("synthetic-access")
                public void execute(Entry<String, ?> e) {
                    logger.trace("createEntity(" + assignedId + ") " + e.getKey() + ": " + e.getValue());
                }
            });
        }

        return assignedId;
    }

    @Override
    public void updateEntity(final E entity) {
        Assert.notNull(entity, "No entity");
        if (!entityExists(entity)) {
            throw new UnsupportedOperationException("updateEntity(" + entity + ") entity not persisted");
        }

        if (logger.isTraceEnabled()) {
            Map<String, ?> props = repo.getProperties(entity);
            ExtendedMapUtils.forAllEntriesDo(props, new Closure<Map.Entry<String, ?>>() {
                @Override
                @SuppressWarnings("synthetic-access")
                public void execute(Entry<String, ?> e) {
                    logger.trace(
                            "updateEntity(" + entity.getId() + ")[" + e.getKey() + "]-BEFORE: " + e.getValue());
                }
            });
        }

        Transformer<E, ? extends Map<String, ?>> transformer = getEntityTransformer(entity);
        if (transformer == null) {
            throw new IllegalStateException("updateEntity(" + entity + ") no properties transformer");
        }

        Map<String, ?> props = repo.setProperties(entity, transformer);
        if (logger.isTraceEnabled()) {
            ExtendedMapUtils.forAllEntriesDo(props, new Closure<Map.Entry<String, ?>>() {
                @Override
                @SuppressWarnings("synthetic-access")
                public void execute(Entry<String, ?> e) {
                    logger.trace("updateEntity(" + entity.getId() + ")[" + e.getKey() + "]-AFTER: " + e.getValue());
                }
            });
        }
    }

    @Override
    public List<String> removeAllIdentifiers(Collection<String> ids) {
        return repo.removeAll(ids);
    }

    @Override
    public E removeEntity(String id) {
        E entity = findEntityById(id);
        if (entity == null) {
            return null;
        }

        repo.removeProperties(id);
        return entity;
    }

    protected IdentifiedEntityIdGenerator<?> getEntityIdGenerator(E entity) {
        if (entity == null) {
            throw new IllegalArgumentException(
                    "getEntityIdGenerator(" + getEntityClass().getSimpleName() + ") no entity");
        }

        return getEntityIdGenerator(entity.getClass());
    }

    @SuppressWarnings("unchecked")
    protected IdentifiedEntityIdGenerator<?> getEntityIdGenerator(Class<?> eClass) {
        IdentifiedEntityIdGenerator<?> generator;
        synchronized (idGeneratorsMap) {
            if ((generator = idGeneratorsMap.get(eClass)) != null) {
                return generator;
            }

            generator = new IdentifiedEntityIdGeneratorImpl<E>((Class<E>) eClass);
            idGeneratorsMap.put(eClass, generator);
        }

        logger.info("getEntityIdGenerator(" + eClass.getSimpleName() + ") created");
        return generator;
    }

    protected Transformer<Map<String, ?>, E> getPropertiesTransformer(E entity) {
        if (entity == null) {
            throw new IllegalArgumentException(
                    "getPropertiesTransformer(" + getEntityClass().getSimpleName() + ") no entity");
        }

        return getPropertiesTransformer(entity.getClass());
    }

    protected Transformer<Map<String, ?>, E> getPropertiesTransformer(Class<?> eClass) {
        Pair<Transformer<Map<String, ?>, E>, ?> pair = getEntityTransformers(eClass);
        return pair.getLeft();
    }

    protected Transformer<E, ? extends Map<String, ?>> getEntityTransformer(E entity) {
        Pair<?, Transformer<E, ? extends Map<String, ?>>> pair = getEntityTransformers(entity);
        return pair.getRight();
    }

    protected Pair<Transformer<Map<String, ?>, E>, Transformer<E, ? extends Map<String, ?>>> getEntityTransformers(
            E entity) {
        if (entity == null) {
            throw new IllegalArgumentException(
                    "getEntityTransformers(" + getEntityClass().getSimpleName() + ") no entity");
        }
        return getEntityTransformers(entity.getClass());
    }

    protected Pair<Transformer<Map<String, ?>, E>, Transformer<E, ? extends Map<String, ?>>> getEntityTransformers(
            final Class<?> eClass) {

        /*
         * NOTE: there might be a "race condition" but we don't care since
         * result is the same either way
         */
        Pair<Transformer<Map<String, ?>, E>, Transformer<E, ? extends Map<String, ?>>> pair;
        synchronized (transformersMap) {
            if ((pair = transformersMap.get(eClass)) != null) {
                return pair;
            }
        }

        Map<String, Pair<Method, Method>> entityAttributes = ExtendedBeanUtils
                .removeNonModifiableAttributes(ExtendedBeanUtils.describeBean(eClass, false, true));
        @SuppressWarnings("unchecked")
        final Transformer<Map<String, ?>, E> pureEntityTransformer = ExtendedBeanUtils.propertiesToBeanTransformer(
                ExtendedConstructorUtils.newInstanceFactory((Class<E>) eClass), entityAttributes);
        Transformer<Map<String, ?>, E> props2entityTransformer = new Transformer<Map<String, ?>, E>() {
            @Override
            public E transform(Map<String, ?> beanProps) {
                Object value = beanProps.remove(CLASS_PROP_NAME);
                if (value == null) {
                    throw new IllegalStateException(
                            "transform(" + eClass.getSimpleName() + ") missing reserved type property");
                }

                return pureEntityTransformer.transform(beanProps);
            }
        };

        @SuppressWarnings("unchecked")
        final Transformer<E, ? extends Map<String, ?>> purePropsTransformer = ExtendedBeanUtils
                .beanToPropertiesTransformer((Class<E>) eClass, entityAttributes);
        Transformer<E, Map<String, ?>> entity2propsTransfomer = new Transformer<E, Map<String, ?>>() {
            @SuppressWarnings({ "unchecked", "rawtypes" })
            @Override
            public Map<String, ?> transform(E input) {
                Map<String, ?> beanProps = purePropsTransformer.transform(input);
                if (ExtendedMapUtils.isEmpty(beanProps)) {
                    return beanProps;
                }

                Object value = beanProps.get(CLASS_PROP_NAME);
                if (value != null) {
                    throw new IllegalStateException(
                            "transform(" + input.getClass().getSimpleName() + ") reserved property used: " + value);
                }
                ((Map) beanProps).put(CLASS_PROP_NAME, eClass);
                return beanProps;
            }
        };

        pair = Pair.<Transformer<Map<String, ?>, E>, Transformer<E, ? extends Map<String, ?>>>of(
                props2entityTransformer, entity2propsTransfomer);
        synchronized (transformersMap) {
            transformersMap.put(eClass, pair);
        }

        logger.info("getEntityTransformers(" + eClass.getSimpleName() + ") created transformers");
        return pair;
    }
}