/**********************************************************************
Copyright (c) 2006 Andy Jefferson and others. All rights reserved.
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.
Contributors:
...
**********************************************************************/
package org.jpox.jpa.metadata;
import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.net.URL;
import java.util.List;
import javax.persistence.PostLoad;
import javax.persistence.PostPersist;
import javax.persistence.PostRemove;
import javax.persistence.PostUpdate;
import javax.persistence.PrePersist;
import javax.persistence.PreRemove;
import javax.persistence.PreUpdate;
import org.jpox.ClassLoaderResolver;
import org.jpox.OMFContext;
import org.jpox.metadata.AbstractClassMetaData;
import org.jpox.metadata.ClassMetaData;
import org.jpox.metadata.EventListenerMetaData;
import org.jpox.metadata.FileMetaData;
import org.jpox.metadata.MetaDataManager;
import org.jpox.metadata.PackageMetaData;
import org.jpox.metadata.SequenceMetaData;
import org.jpox.metadata.TableGeneratorMetaData;
import org.jpox.metadata.xml.MetaDataParser;
import org.jpox.util.JPOXLogger;
/**
* Manager of JPA MetaData information in JPOX.
* Manages the MetaData for a particular "persistence-unit".
*
* @version $Revision: 1.1 $
*/
public class JPAMetaDataManager extends MetaDataManager
{
/**
* Constructor.
* @param ctxt ObjectManagerFactory Context that this metadata manager operates in
*/
public JPAMetaDataManager(OMFContext ctxt)
{
super(ctxt);
// log the configuration
if (JPOXLogger.METADATA.isDebugEnabled())
{
logConfiguration();
}
}
/**
* Utility to parse a file, using the "jpa" MetaData handler.
* @param fileURL URL of the file
* @return The FileMetaData for this file
*/
protected FileMetaData parseFile(URL fileURL)
{
if (metaDataParser == null)
{
metaDataParser = new MetaDataParser(this, validateMetaData);
}
return (FileMetaData)metaDataParser.parseMetaDataURL(fileURL, "jpa");
}
/**
* Method to take the FileMetaData and register the relevant parts of it with the various
* convenience collections/maps that we use for access.
* @param fileURLString URL of the metadata file
* @param filemd The File MetaData
*/
@SuppressWarnings("unchecked")
public void registerFile(String fileURLString, FileMetaData filemd, ClassLoaderResolver clr)
{
if (fileURLString == null)
{
// Null file
return;
}
if (fileMetaDataByURLString.get(fileURLString) != null)
{
// Already registered!
return;
}
fileMetaDataByURLString.put(fileURLString, filemd);
registerQueriesForFile(filemd);
registerSequencesForFile(filemd);
registerTableGeneratorsForFile(filemd);
registerQueryResultMetaDataForFile(filemd);
if (filemd.getListeners() != null)
{
List fileListeners = filemd.getListeners();
listeners.addAll(fileListeners);
for (int i=0; i<fileListeners.size(); i++)
{
EventListenerMetaData elmd = (EventListenerMetaData) fileListeners.get(i);
// Load up all listener methods of the listener
// If the metadata had defined some methods then the annotated method definition will be ignored
populateListenerMethodsForEventListener(elmd, clr);
}
}
// Register the classes and interfaces for later use
if (filemd.getType() == FileMetaData.JPA_FILE ||
filemd.getType() == FileMetaData.ANNOTATIONS)
{
for (int i = 0; i < filemd.getNoOfPackages(); i++)
{
PackageMetaData pmd = filemd.getPackage(i);
// Register all classes into the respective lookup maps
for (int j = 0; j < pmd.getNoOfClasses(); j++)
{
ClassMetaData cmd = pmd.getClass(j);
if (cmd.getEntityName() != null)
{
// Register the ClassMetaData for the implementation
classMetaDataByEntityName.put(cmd.getEntityName(), cmd);
}
registerMetaDataForClass(cmd.getFullClassName(), cmd);
if (cmd.getListeners() != null)
{
List classListeners = cmd.getListeners();
for (int k=0; k<classListeners.size(); k++)
{
EventListenerMetaData elmd = (EventListenerMetaData) classListeners.get(k);
// Load up all listener methods of the listener
// If the metadata had defined some methods then the annotated method definition will be ignored
populateListenerMethodsForEventListener(elmd, clr);
}
}
}
}
}
}
/**
* Method to populate the methods of the listener class into the EventListenerMetaData.
* Checks the annotations of the listener class itself and adds them in to the definition that
* the EventListener uses.
* @param elmd EventListenerMetaData (updated by this method)
* @param clr ClassLoader resolver
*/
private void populateListenerMethodsForEventListener(EventListenerMetaData elmd, ClassLoaderResolver clr)
{
Class listenerClass = clr.classForName(elmd.getClassName());
populateListenerMethodsForClassInEventListener(elmd, listenerClass, clr);
}
/**
* Convenience method to process the specified class for listener callback methods adding them to
* the EventListener. Proceeds up to the superclass if there is one.
* @param elmd EventListenerMetaData to add the methods to.
* @param cls The class to process for callback-annotated methods
* @param clr ClassLoader resolver
*/
private void populateListenerMethodsForClassInEventListener(EventListenerMetaData elmd, Class cls,
ClassLoaderResolver clr)
{
Method[] methods = cls.getDeclaredMethods();
if (methods != null)
{
for (int i=0;i<methods.length;i++)
{
Annotation[] methodAnnots = methods[i].getAnnotations();
if (methodAnnots != null)
{
for (int j=0;j<methodAnnots.length;j++)
{
if (methodAnnots[j].annotationType() == PrePersist.class ||
methodAnnots[j].annotationType() == PostPersist.class ||
methodAnnots[j].annotationType() == PreRemove.class ||
methodAnnots[j].annotationType() == PostRemove.class ||
methodAnnots[j].annotationType() == PreUpdate.class ||
methodAnnots[j].annotationType() == PostUpdate.class ||
methodAnnots[j].annotationType() == PostLoad.class)
{
elmd.addCallback(methodAnnots[j].annotationType().getName(),
methods[i].getDeclaringClass().getName(), methods[i].getName());
}
}
}
}
}
// Go up to superclass if there is one
if (cls.getSuperclass() == null || cls.getSuperclass() == Object.class)
{
return;
}
populateListenerMethodsForClassInEventListener(elmd, cls.getSuperclass(), clr);
}
/**
* Convenience method to register all sequences found in the passed file.
* @param filemd MetaData for the file
*/
@SuppressWarnings("unchecked")
protected void registerSequencesForFile(FileMetaData filemd)
{
// Register all sequences for the packages in this file
for (int i=0;i<filemd.getNoOfPackages();i++)
{
PackageMetaData pmd = filemd.getPackage(i);
SequenceMetaData[] seqmds = pmd.getSequences();
if (seqmds != null)
{
for (int j=0;j<seqmds.length;j++)
{
// Register using its basic sequence name
sequenceMetaDataByPackageSequence.put(seqmds[j].getName(), seqmds[j]);
}
}
}
}
/**
* Convenience method to register all table generators found in the passed file.
* @param filemd MetaData for the file
*/
@SuppressWarnings("unchecked")
protected void registerTableGeneratorsForFile(FileMetaData filemd)
{
// Register all table generators for the packages in this file
for (int i=0;i<filemd.getNoOfPackages();i++)
{
PackageMetaData pmd = filemd.getPackage(i);
TableGeneratorMetaData[] tgmds = pmd.getTableGenerators();
if (tgmds != null)
{
for (int j=0;j<tgmds.length;j++)
{
// Register using its basic sequence name
tableGeneratorMetaDataByPackageSequence.put(tgmds[j].getName(), tgmds[j]);
}
}
}
}
/**
* Accessor for the JPA MetaData for a class.
* With JPA we either register the classes in "persistence.xml" (via "class", "jar-file"
* or "mapping-file") or we have them annotated. If they havent been loaded when we
* loaded "persistence.xml" then we only check for annotations in that class.
* @param c The class to find MetaData for
* @return The ClassMetaData for this class (or null if not found)
**/
@SuppressWarnings("unchecked")
public synchronized AbstractClassMetaData getMetaDataForClassInternal(Class c, ClassLoaderResolver clr)
{
// If we know that this class/interface has no MetaData/annotations don't bother searching
if (isClassWithoutPersistenceInfo(c.getName()))
{
return null;
}
// Check for MetaData loaded when we loaded the "persistence-unit"
AbstractClassMetaData acmd = (AbstractClassMetaData)classMetaDataByClass.get(c.getName());
if (acmd != null)
{
return acmd;
}
// No MetaData so check for annotations
FileMetaData annFilemd = loadAnnotationsForClass(c, clr, true, true);
if (annFilemd != null)
{
// No MetaData but annotations present so use that
return annFilemd.getPackage(0).getClass(0);
}
// Not found, so add to known classes/interfaces without MetaData
if (JPOXLogger.METADATA.isDebugEnabled())
{
JPOXLogger.METADATA.debug(LOCALISER.msg("044043", c.getName()));
}
classesWithoutPersistenceInfo.add(c.getName());
return null;
}
}