com.impetus.kundera.metadata.MetadataManager.java Source code

Java tutorial

Introduction

Here is the source code for com.impetus.kundera.metadata.MetadataManager.java

Source

/*
 * Copyright 2010 Impetus Infotech.
 *
 * 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 com.impetus.kundera.metadata;

import java.lang.reflect.Field;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

import javax.persistence.EntityManagerFactory;
import javax.persistence.ManyToMany;
import javax.persistence.ManyToOne;
import javax.persistence.OneToMany;
import javax.persistence.OneToOne;
import javax.persistence.PersistenceException;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import com.impetus.kundera.classreading.AnnotationDiscoveryListener;
import com.impetus.kundera.metadata.EntityMetadata.Relation;
import com.impetus.kundera.metadata.processor.CacheableAnnotationProcessor;
import com.impetus.kundera.metadata.processor.DocumentProcessor;
import com.impetus.kundera.metadata.processor.ColumnFamilyProcessor;
import com.impetus.kundera.metadata.processor.EntityListenersProcessor;
import com.impetus.kundera.metadata.processor.IndexProcessor;
import com.impetus.kundera.metadata.processor.SuperColumnFamilyProcessor;
import com.impetus.kundera.utils.ReflectUtils;

/**
 * Concrete implementation of IMetadataManager.
 * 
 * @author animesh.kumar
 */
public class MetadataManager implements AnnotationDiscoveryListener {

    /** the log used by this class. */
    private static Log log = LogFactory.getLog(MetadataManager.class);

    /** cache for Metadata. */
    private Map<Class<?>, EntityMetadata> metadataCache = new ConcurrentHashMap<Class<?>, EntityMetadata>();

    /** The entity name to class map. */
    private Map<String, Class<?>> entityNameToClassMap = new ConcurrentHashMap<String, Class<?>>();

    /** The metadata processors. */
    private List<MetadataProcessor> metadataProcessors;

    /** The Validator. */
    private Validator validator;

    // intentionally unused!
    /** The factory. */
    @SuppressWarnings("unused")
    private EntityManagerFactory factory;

    // set after build is called?
    /** The instantiated. */
    private boolean instantiated = false;

    /**
     * Instantiates a new metadata manager.
     * 
     * @param factory
     *            the factory
     */
    public MetadataManager(EntityManagerFactory factory) {
        this.factory = factory;

        validator = new ValidatorImpl();

        metadataProcessors = new ArrayList<MetadataProcessor>();

        // add processors to chain.
        metadataProcessors.add(new SuperColumnFamilyProcessor(factory));
        metadataProcessors.add(new ColumnFamilyProcessor(factory));
        metadataProcessors.add(new DocumentProcessor(factory));
        metadataProcessors.add(new CacheableAnnotationProcessor());
        metadataProcessors.add(new IndexProcessor());
        metadataProcessors.add(new EntityListenersProcessor());
    }

    /**
     * Validate.
     * 
     * @param clazz
     *            the clazz
     * 
     * @throws PersistenceException
     *             the persistence exception
     */
    public final void validate(Class<?> clazz) throws PersistenceException {
        validator.validate(clazz);
    }

    /**
     * Checks if is column family.
     * 
     * @param clazz
     *            the clazz
     * 
     * @return true, if is column family
     * 
     * @throws PersistenceException
     *             the persistence exception
     */
    public final boolean isColumnFamily(Class<?> clazz) {
        return getEntityMetadata(clazz).getType().equals(EntityMetadata.Type.COLUMN_FAMILY);
    }

    /**
     * Checks if is super column family.
     * 
     * @param clazz
     *            the clazz
     * 
     * @return true, if is super column family
     * 
     * @throws PersistenceException
     *             the persistence exception
     */
    public final boolean isSuperColumnFamily(Class<?> clazz) {
        return getEntityMetadata(clazz).getType().equals(EntityMetadata.Type.SUPER_COLUMN_FAMILY);
    }

    /**
     * Gets the entity metadata.
     * 
     * @param clazz
     *            the clazz
     * 
     * @return the entity metadata
     * 
     * @throws PersistenceException
     *             the persistence exception
     */
    public final EntityMetadata getEntityMetadata(Class<?> clazz) {

        EntityMetadata metadata = metadataCache.get(clazz);
        if (null == metadata) {
            log.debug("Metadata not found in cache for " + clazz.getName());
            // double check locking.
            synchronized (clazz) {
                if (null == metadata) {
                    metadata = process(clazz);
                    cacheMetadata(clazz, metadata);
                }
            }
        }
        return metadata;
    }

    // helper methods to strip CGLIB from class

    /**
     * Process.
     * 
     * @param clazz
     *            the clazz
     * 
     * @return the entity metadata
     * 
     * @throws PersistenceException
     *             the persistence exception
     */
    private EntityMetadata process(Class<?> clazz) {

        EntityMetadata metadata = new EntityMetadata(clazz);
        validate(clazz);

        log.debug("Processing @Entity >> " + clazz);

        for (MetadataProcessor processor : metadataProcessors) {
            processor.process(clazz, metadata);
        }

        return metadata;
    }

    /**
     * Cache metadata.
     * 
     * @param clazz
     *            the clazz
     * @param metadata
     *            the metadata
     */
    private void cacheMetadata(Class<?> clazz, EntityMetadata metadata) {
        metadataCache.put(clazz, metadata);

        // save name to class map.
        if (entityNameToClassMap.containsKey(clazz.getSimpleName())) {
            throw new PersistenceException("Name conflict between classes "
                    + entityNameToClassMap.get(clazz.getSimpleName()).getName() + " and " + clazz.getName());
        }
        entityNameToClassMap.put(clazz.getSimpleName(), clazz);
    }

    /**
     * Gets the entity class by name.
     * 
     * @param name
     *            the name
     * 
     * @return the entity class by name
     */
    public final Class<?> getEntityClassByName(String name) {
        return entityNameToClassMap.get(name);
    }

    /**
     * Gets the entity metadatas as list.
     * 
     * @return the entity metadatas as list
     */
    public final List<EntityMetadata> getEntityMetadatasAsList() {
        return Collections.unmodifiableList(new ArrayList<EntityMetadata>(metadataCache.values()));
    }

    /*
     * @see
     * com.impetus.kundera.classreading.AnnotationDiscoveryListener#discovered
     * (java.lang.String, java.lang.String[])
     */
    @Override
    // called whenever a class with @Entity annotation is encountered in the
    // classpath.
    public final void discovered(String className, String[] annotations) {
        try {
            Class<?> clazz = Class.forName(className);

            // process for Metadata
            EntityMetadata metadata = process(clazz);
            cacheMetadata(clazz, metadata);
            log.info("Added @Entity " + clazz.getName());
        } catch (ClassNotFoundException e) {
            throw new PersistenceException(e.getMessage());
        }
    }

    /**
     * Build Inter/Intra @Entity relationships.
     */
    public void build() {
        log.debug("Building @Entity's foreign relations.");
        for (EntityMetadata metadata : getEntityMetadatasAsList()) {
            processRelations(metadata.getEntityClazz());
            log.debug("Metadata for @Entity " + metadata.getEntityClazz() + "\n" + metadata);
        }
        instantiated = true;
    }

    /**
     * Helper class to scan each @Entity class and build various relational annotation.
     * 
     * @param entity
     *            the entity
     */
    private void processRelations(Class<?> entity) {
        EntityMetadata metadata = getEntityMetadata(entity);

        for (Field f : entity.getDeclaredFields()) {

            // OneToOne
            if (f.isAnnotationPresent(OneToOne.class)) {
                // taking field's type as foreign entity, ignoring
                // "targetEntity"
                Class<?> targetEntity = f.getType();
                try {
                    validate(targetEntity);
                    OneToOne ann = f.getAnnotation(OneToOne.class);

                    Relation relation = metadata.new Relation(f, targetEntity, null, ann.fetch(),
                            Arrays.asList(ann.cascade()), ann.optional(), ann.mappedBy(),
                            EntityMetadata.ForeignKey.ONE_TO_ONE);

                    metadata.addRelation(f.getName(), relation);
                } catch (PersistenceException pe) {
                    throw new PersistenceException("Error with @OneToOne in @Entity(" + entity.getName() + "."
                            + f.getName() + "), reason: " + pe.getMessage());
                }
            }

            // OneToMany
            else if (f.isAnnotationPresent(OneToMany.class)) {

                OneToMany ann = f.getAnnotation(OneToMany.class);

                Class<?> targetEntity = null;

                // resolve from generic parameters
                Type[] parameters = ReflectUtils.getTypeArguments(f);
                if (parameters != null) {
                    if (parameters.length == 1) {
                        targetEntity = (Class<?>) parameters[0];
                    } else {
                        throw new PersistenceException("How many parameters man?");
                    }
                }
                // now, check annotations
                if (null != ann.targetEntity() && !ann.targetEntity().getSimpleName().equals("void")) {
                    targetEntity = ann.targetEntity();
                }

                try {
                    validate(targetEntity);
                    Relation relation = metadata.new Relation(f, targetEntity, f.getType(), ann.fetch(),
                            Arrays.asList(ann.cascade()), Boolean.TRUE, ann.mappedBy(),
                            EntityMetadata.ForeignKey.ONE_TO_MANY);

                    metadata.addRelation(f.getName(), relation);
                } catch (PersistenceException pe) {
                    throw new PersistenceException("Error with @OneToMany in @Entity(" + entity.getName() + "."
                            + f.getName() + "), reason: " + pe.getMessage());
                }
            }

            // ManyToOne
            else if (f.isAnnotationPresent(ManyToOne.class)) {
                // taking field's type as foreign entity, ignoring
                // "targetEntity"
                Class<?> targetEntity = f.getType();
                try {
                    validate(targetEntity);
                    ManyToOne ann = f.getAnnotation(ManyToOne.class);

                    Relation relation = metadata.new Relation(f, targetEntity, null, ann.fetch(),
                            Arrays.asList(ann.cascade()), ann.optional(), null, // mappedBy is null
                            EntityMetadata.ForeignKey.MANY_TO_ONE);

                    metadata.addRelation(f.getName(), relation);
                } catch (PersistenceException pe) {
                    throw new PersistenceException("Error with @OneToOne in @Entity(" + entity.getName() + "."
                            + f.getName() + "), reason: " + pe.getMessage());
                }
            }

            // ManyToMany
            else if (f.isAnnotationPresent(ManyToMany.class)) {

                ManyToMany ann = f.getAnnotation(ManyToMany.class);

                Class<?> targetEntity = null;

                // resolve from generic parameters
                Type[] parameters = ReflectUtils.getTypeArguments(f);
                if (parameters != null) {
                    if (parameters.length == 1) {
                        targetEntity = (Class<?>) parameters[0];
                    } else {
                        throw new PersistenceException("How many parameters man?");
                    }
                }
                // now, check annotations
                if (null != ann.targetEntity() && !ann.targetEntity().getSimpleName().equals("void")) {
                    targetEntity = ann.targetEntity();
                }

                try {
                    validate(targetEntity);
                    Relation relation = metadata.new Relation(f, targetEntity, f.getType(), ann.fetch(),
                            Arrays.asList(ann.cascade()), Boolean.TRUE, ann.mappedBy(),
                            EntityMetadata.ForeignKey.MANY_TO_MANY);

                    metadata.addRelation(f.getName(), relation);
                } catch (PersistenceException pe) {
                    throw new PersistenceException("Error with @OneToMany in @Entity(" + entity.getName() + "."
                            + f.getName() + "), reason: " + pe.getMessage());
                }
            }

        }
    }
}