Java tutorial
/* * 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()); } } } } }