OPENJPA-1010: MetaDataRespository knows about meta-class. The naming and support for meta-class added to MetaDataFactory. Implemented in PersistenceMetaDataFactory. AnnotationProcessor6 aligned with this naming policy interface.

git-svn-id: https://svn.apache.org/repos/asf/openjpa/trunk@772820 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
Pinaki Poddar 2009-05-07 23:57:04 +00:00
parent 195525392c
commit 9a8246799c
13 changed files with 294 additions and 40 deletions

View File

@ -2037,7 +2037,7 @@ public class ClassMetaData
try {
oid.getConstructor((Class[]) null);
} catch (Exception e) {
throw new MetaDataException(_loc.get("null-cons", _type)).
throw new MetaDataException(_loc.get("null-cons", oid, _type)).
setCause(e);
}

View File

@ -126,4 +126,19 @@ public class DelegatingMetaDataFactory
public void loadXMLMetaData(FieldMetaData fmd) {
_delegate.loadXMLMetaData(fmd);
}
public String getMetaModelClassName(String managedClassName) {
return _delegate.getMetaModelClassName(managedClassName);
}
public String getManagedClassName(String metamodelClassName) {
return _delegate.getManagedClassName(metamodelClassName);
}
public boolean isMetaClass(Class<?> c) {
return _delegate.isMetaClass(c);
}
public Class<?> getManagedClass(Class<?> c) {
return _delegate.getManagedClass(c);
}
}

View File

@ -31,6 +31,7 @@ import org.apache.openjpa.lib.meta.ClassArgParser;
*
* @author Patrick Linskey
* @author Abe White
* @author Pinaki Poddar
*/
public interface MetaDataFactory
extends MetaDataModes {
@ -151,4 +152,37 @@ public interface MetaDataFactory
* metadata should be added directly to the repository.
*/
public void loadXMLMetaData(FieldMetaData fmd);
/**
* Gets the name of the meta-model class for the given fully-qualified
* managed class name.
*
* @since 2.0.0
*/
public String getMetaModelClassName(String managedClassName);
/**
* Gets the name of the managed class for the given fully-qualified
* meta-model class name.
*
* @since 2.0.0
*/
public String getManagedClassName(String metamodelClassName);
/**
* Affirms if the given class is a meta-class.
*
* @since 2.0.0
*/
public boolean isMetaClass(Class<?> c);
/**
* Gets the managed class corresponding to the given meta-class.
*
* @return null if the given input is not a meta-class.
*
* @since 2.0.0
*/
public Class<?> getManagedClass(Class<?> c);
}

View File

@ -124,6 +124,8 @@ public class MetaDataRepository
.synchronizedMap(new HashMap<Class<?>,NonPersistentMetaData>());
private final Map<Class<?>,NonPersistentMetaData> _nonMapped = Collections
.synchronizedMap(new HashMap<Class<?>,NonPersistentMetaData>());
private final Map<Class<?>, Class<?>> _metamodel = Collections
.synchronizedMap(new HashMap<Class<?>, Class<?>>());
// map of classes to lists of their subclasses
private final Map<Class<?>,List<Class<?>>> _subs =
@ -1321,6 +1323,10 @@ public class MetaDataRepository
Class<?> cls;
for (String className : names) {
cls = classForName(className, clsLoader);
if (_factory.isMetaClass(cls)) {
setMetaModel(cls);
continue;
}
if (cls != null) {
classes.add(cls);
@ -1589,6 +1595,53 @@ public class MetaDataRepository
}
}
/**
* Puts the meta class corresponding to the given entity class.
*/
public void setMetaModel(Class<?> m2) {
Class<?> cls = _factory.getManagedClass(m2);
if (cls != null)
_metamodel.put(cls, m2);
}
/**
* Puts the meta class corresponding to the given persistent class.
*/
public void setMetaModel(ClassMetaData meta, Class<?> m2) {
_metamodel.put(meta.getDescribedType(), m2);
}
/**
* Gets the meta class corresponding to the given persistent class.
*/
public Class<?> getMetaModel(ClassMetaData meta, boolean load) {
return getMetaModel(meta.getDescribedType(), load);
}
/**
* Gets the meta class corresponding to the given class.
* If load is false, returns the meta class if has been set for the given
* persistent class earlier.
* If the load is true then also attempts to apply the current
* naming policy to derive meta class name and attempts to load the meta
* class.
*/
public Class<?> getMetaModel(Class<?> entity, boolean load) {
if (_metamodel.containsKey(entity))
return _metamodel.get(entity);
String m2 = _factory.getMetaModelClassName(entity.getName());
try {
Class<?> m2cls = J2DoPrivHelper.getForNameAction(m2, true,
entity.getClassLoader()).run();
_metamodel.put(entity, m2cls);
return m2cls;
} catch (Throwable t) {
if (_log.isWarnEnabled())
_log.warn(_loc.get("meta-no-model", m2, entity, t));
}
return null;
}
///////////////////////////////
// Configurable implementation
///////////////////////////////

View File

@ -142,4 +142,19 @@ public class NoneMetaDataFactory
public void loadXMLMetaData(FieldMetaData fmd) {
}
public String getMetaModelClassName(String managedClassName) {
return null;
}
public String getManagedClassName(String metamodelClassName) {
return null;
}
public boolean isMetaClass(Class<?> c) {
return false;
}
public Class<?> getManagedClass(Class<?> c) {
return null;
}
}

View File

@ -187,7 +187,7 @@ invalid-id: The id class specified by type "{0}" does not match the \
primary key fields of the class. Make sure your identity class has the \
same primary keys as your persistent type, including pk field types. \
Mismatched property: "{1}"
null-cons: The id class specified by type "{0}" does not have a public \
null-cons: The id class "{0}" specified by type "{1}" does not have a public \
no-args constructor.
hc-method: The identity class specified by type "{0}" is not valid, as the \
hashCode method is not overridden. Implement hashCode so that two \

View File

@ -19,8 +19,12 @@
package org.apache.openjpa.persistence.meta;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import javax.persistence.metamodel.Attribute;
import javax.persistence.metamodel.Collection;
import javax.persistence.metamodel.Entity;
import javax.persistence.metamodel.List;
import javax.persistence.metamodel.ManagedType;
import javax.persistence.metamodel.Set;
@ -30,6 +34,9 @@ import javax.persistence.metamodel.Bindable.BindableType;
import javax.persistence.metamodel.Type.PersistenceType;
import org.apache.openjpa.meta.ClassMetaData;
import org.apache.openjpa.meta.MetaDataRepository;
import org.apache.openjpa.persistence.criteria.Account;
import org.apache.openjpa.persistence.criteria.Account_;
import org.apache.openjpa.persistence.test.SingleEMFTestCase;
/**
@ -40,9 +47,11 @@ import org.apache.openjpa.persistence.test.SingleEMFTestCase;
*/
public class TestMetamodel extends SingleEMFTestCase {
MetamodelImpl model;
MetaDataRepository repos;
public void setUp() {
super.setUp(
Account.class,
ImplicitFieldAccessMappedSuperclass.class,
ImplicitFieldAccessBase.class,
ImplicitFieldAccessSubclass.class,
@ -51,15 +60,31 @@ public class TestMetamodel extends SingleEMFTestCase {
Embed0.class,
Embed1.class);
emf.createEntityManager();
repos = emf.getConfiguration().getMetaDataRepositoryInstance();
model = (MetamodelImpl)emf.getMetamodel();
}
public void testModelIsPopulated() {
public void testModelIsInstantiated() {
assertFalse(model.getEntities().isEmpty());
assertFalse(model.getEmbeddables().isEmpty());
assertFalse(model.getManagedTypes().isEmpty());
}
public void testModelIsPopulated() {
Entity<Account> m = model.entity(Account.class);
Class<?> mCls = m.getJavaType();
assertNotNull(m);
Class<?> m2Cls = repos.getMetaModel(mCls, true);
assertNotNull(m2Cls);
try {
Field f2 = getStaticField(m2Cls, "balance");
assertNotNull(f2);
} catch (Throwable t) {
t.printStackTrace();
fail();
}
}
public void testPersistentCategory() {
assertCategory(PersistenceType.MAPPED_SUPERCLASS,
ImplicitFieldAccessMappedSuperclass.class);
@ -199,4 +224,19 @@ public class TestMetamodel extends SingleEMFTestCase {
void assertCategory(PersistenceType category, Class<?> cls) {
assertEquals(category, categorize(cls));
}
Field getStaticField(Class<?> cls, String name) {
try {
System.err.println("Fields of " + cls);
Field[] fds = cls.getDeclaredFields();
for (Field f : fds) {
System.err.println(f);
int mods = f.getModifiers();
if (f.getName().equals(name) && Modifier.isStatic(mods))
return f;
}
} catch (Exception e) {
}
return null;
}
}

View File

@ -38,6 +38,7 @@ import javax.persistence.NamedQueries;
import javax.persistence.NamedQuery;
import javax.persistence.SqlResultSetMapping;
import javax.persistence.SqlResultSetMappings;
import javax.persistence.metamodel.TypesafeMetamodel;
import org.apache.openjpa.lib.conf.Configurable;
import org.apache.openjpa.lib.conf.Configuration;
@ -538,4 +539,31 @@ public class PersistenceMetaDataFactory
= getXMLAnnotationParser();
parser.parse(fmd);
}
private static String UNDERSCORE = "_";
public String getManagedClassName(String mmClassName) {
if (mmClassName == null || mmClassName.length() == 0)
return null;
if (mmClassName.endsWith(UNDERSCORE))
return mmClassName.substring(0, mmClassName.length()-1);
return mmClassName;
}
public String getMetaModelClassName(String managedClassName) {
if (managedClassName == null || managedClassName.length() == 0)
return null;
return managedClassName + UNDERSCORE;
}
public boolean isMetaClass(Class<?> c) {
return c != null && c.getAnnotation(TypesafeMetamodel.class) != null;
}
public Class<?> getManagedClass(Class<?> c) {
if (isMetaClass(c)) {
return c.getAnnotation(TypesafeMetamodel.class).value();
}
return null;
}
}

View File

@ -1,3 +1,21 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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.apache.openjpa.persistence.meta;
import static javax.lang.model.SourceVersion.RELEASE_6;
@ -14,7 +32,6 @@ import java.util.Set;
import javax.annotation.Generated;
import javax.annotation.processing.AbstractProcessor;
import javax.annotation.processing.Filer;
import javax.annotation.processing.ProcessingEnvironment;
import javax.annotation.processing.RoundEnvironment;
import javax.annotation.processing.SupportedAnnotationTypes;
@ -25,7 +42,6 @@ import javax.lang.model.element.TypeElement;
import javax.lang.model.type.TypeMirror;
import javax.lang.model.util.Elements;
import javax.persistence.metamodel.TypesafeMetamodel;
import javax.tools.Diagnostic;
import javax.tools.JavaCompiler;
import javax.tools.JavaFileObject;
import javax.tools.StandardJavaFileManager;
@ -33,6 +49,8 @@ import javax.tools.StandardLocation;
import javax.tools.ToolProvider;
import org.apache.openjpa.lib.util.Localizer;
import org.apache.openjpa.meta.MetaDataFactory;
import org.apache.openjpa.persistence.PersistenceMetaDataFactory;
import org.apache.openjpa.persistence.util.SourceCode;
/**
@ -42,6 +60,8 @@ import org.apache.openjpa.persistence.util.SourceCode;
* This tool is invoked during compilation for JDK6 compiler if OpenJPA and JPA
* libraries are specified in the compiler <code>-processorpath</code> option.
* <br>
* Supported options
* <LI>
* For example,<br>
* <center><code>$ javac -processorpath path/to/openjpa;/path/to/jpa
* -s src -Alog mypackage/MyClass.java</code></center>
@ -65,15 +85,17 @@ import org.apache.openjpa.persistence.util.SourceCode;
"javax.persistence.Entity",
"javax.persistence.Embeddable",
"javax.persistence.MappedSuperclass" })
@SupportedOptions( { "log", "out", "source" })
@SupportedOptions( { "log", "out", "source", "naming" })
@SupportedSourceVersion(RELEASE_6)
public class AnnotationProcessor6 extends AbstractProcessor {
private SourceAnnotationHandler handler;
private StandardJavaFileManager fileManager;
private MetaDataFactory factory;
private int generatedSourceVersion = 6;
private CompileTimeLogger logger;
private static Localizer _loc =
Localizer.forPackage(AnnotationProcessor6.class);
private static final String UNDERSCORE = "_";
/**
* Category of members as per JPA 2.0 type system.
@ -147,10 +169,12 @@ public class AnnotationProcessor6 extends AbstractProcessor {
@Override
public synchronized void init(ProcessingEnvironment processingEnv) {
super.init(processingEnv);
processingEnv.getMessager().printMessage(Diagnostic.Kind.NOTE,
_loc.get("mmg-tool-banner").getMessage());
logger = new CompileTimeLogger(processingEnv);
handler = new SourceAnnotationHandler(processingEnv);
logger.info(_loc.get("mmg-tool-banner"));
setSourceVersion();
setFileManager();
setNamingPolicy();
handler = new SourceAnnotationHandler(processingEnv, logger);
}
/**
@ -181,14 +205,17 @@ public class AnnotationProcessor6 extends AbstractProcessor {
Elements eUtils = processingEnv.getElementUtils();
String originalClass = eUtils.getBinaryName((TypeElement) e).toString();
String originalSimpleClass = e.getSimpleName().toString();
String metaClass = originalClass + UNDERSCORE;
String metaClass = factory.getMetaModelClassName(originalClass);
SourceCode source = new SourceCode(metaClass);
comment(source);
annotate(source, originalClass);
TypeElement supCls = handler.getPersistentSupertype(e);
if (supCls != null)
source.getTopLevelClass().setSuper(supCls.toString() + UNDERSCORE);
if (supCls != null) {
String superName = factory.getMetaModelClassName(
supCls.toString());
source.getTopLevelClass().setSuper(superName);
}
try {
PrintWriter writer = createSourceFile(metaClass, e);
SourceCode.Class modelClass = source.getTopLevelClass();
@ -244,7 +271,7 @@ public class AnnotationProcessor6 extends AbstractProcessor {
SourceCode.Class cls = source.getTopLevelClass();
cls.addAnnotation(TypesafeMetamodel.class.getName())
.addArgument("value", originalClass + ".class", false);
if (getSourceVersion() >= 6) {
if (generatedSourceVersion >= 6) {
cls.addAnnotation(Generated.class.getName())
.addArgument("value", this.getClass().getName())
.addArgument("date", new Date().toString());
@ -260,33 +287,57 @@ public class AnnotationProcessor6 extends AbstractProcessor {
* the source version for the generated classes.
* n must be a integer. Default or wrong specification returns 6.
*/
private int getSourceVersion() {
private void setSourceVersion() {
String version = processingEnv.getOptions().get("source");
if (version != null) {
try {
return Integer.parseInt(version);
generatedSourceVersion = Integer.parseInt(version);
} catch (NumberFormatException e) {
logger.warn(_loc.get("mmg-bad-source", version, 6));
generatedSourceVersion = 6;
}
} else {
generatedSourceVersion = 6;
}
}
private void setNamingPolicy() {
String policy = processingEnv.getOptions().get("naming");
if (policy != null) {
try {
factory = (MetaDataFactory)Class.forName(policy).newInstance();
} catch (Throwable e) {
logger.warn(_loc.get("mmg-bad-naming", policy, e));
factory = new PersistenceMetaDataFactory();
}
} else {
factory = new PersistenceMetaDataFactory();
}
}
private void setFileManager() {
JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
fileManager = compiler.getStandardFileManager(null,
null, null);
String srcOutput = processingEnv.getOptions().get("out");
if (srcOutput != null) {
try {
fileManager.setLocation(StandardLocation.SOURCE_OUTPUT,
Collections.singletonList(new File(srcOutput)));
} catch (Throwable t) {
logger.warn(_loc.get("mmg-bad-out", srcOutput,
StandardLocation.SOURCE_OUTPUT));
}
}
return 6;
}
private PrintWriter createSourceFile(String metaClass, TypeElement e)
throws IOException {
JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
StandardJavaFileManager mgr = compiler.getStandardFileManager(null,
null, null);
String srcOutput = processingEnv.getOptions().get("out");
if (srcOutput != null) {
mgr.setLocation(StandardLocation.SOURCE_OUTPUT,
Collections.singletonList(new File(srcOutput)));
}
JavaFileObject javaFile = mgr.getJavaFileForOutput(
JavaFileObject javaFile = fileManager.getJavaFileForOutput(
StandardLocation.SOURCE_OUTPUT,
metaClass, JavaFileObject.Kind.SOURCE, null);
logger.info(_loc.get("mmg-process", javaFile.toUri()).getMessage());
logger.info(_loc.get("mmg-process", javaFile.toUri()));
OutputStream out = javaFile.openOutputStream();
PrintWriter writer = new PrintWriter(out);
return writer;

View File

@ -22,6 +22,8 @@ import javax.annotation.processing.Messager;
import javax.annotation.processing.ProcessingEnvironment;
import javax.tools.Diagnostic;
import org.apache.openjpa.lib.util.Localizer;
/**
* Simple logger sets log level from javac compilers annotation processing
* options <code>-Alog=TRACE|INFO|WARN|ERROR</code> and uses the processing
@ -32,10 +34,20 @@ import javax.tools.Diagnostic;
*/
public class CompileTimeLogger {
private static enum Level {TRACE, INFO, WARN, ERROR};
private static Localizer _loc = Localizer.forPackage(
CompileTimeLogger.class);
private static Level DEFAULT_LEVEL = Level.WARN;
private int logLevel;
private Messager messager;
public CompileTimeLogger(ProcessingEnvironment env) {
messager = env.getMessager();
String level = env.getOptions().get("log");
if (level == null) {
logLevel = DEFAULT_LEVEL.ordinal();
return;
}
if ("trace".equalsIgnoreCase(level))
logLevel = Level.TRACE.ordinal();
else if ("info".equalsIgnoreCase(level))
@ -45,32 +57,31 @@ public class CompileTimeLogger {
else if ("error".equalsIgnoreCase(level))
logLevel = Level.ERROR.ordinal();
else {
logLevel = Level.INFO.ordinal();
warn("mmg-bad-log");
logLevel = DEFAULT_LEVEL.ordinal();
warn(_loc.get("mmg-bad-log", level, DEFAULT_LEVEL));
}
messager = env.getMessager();
}
public void info(String message) {
public void info(Localizer.Message message) {
log(Level.INFO, message, Diagnostic.Kind.NOTE);
}
public void trace(String message) {
public void trace(Localizer.Message message) {
log(Level.TRACE, message, Diagnostic.Kind.NOTE);
}
public void warn(String message) {
public void warn(Localizer.Message message) {
log(Level.WARN, message, Diagnostic.Kind.MANDATORY_WARNING);
}
public void error(String message) {
public void error(Localizer.Message message) {
log(Level.ERROR, message, Diagnostic.Kind.ERROR);
}
private void log(Level level, String message, Diagnostic.Kind kind) {
private void log(Level level, Localizer.Message message,
Diagnostic.Kind kind) {
if (logLevel <= level.ordinal()) {
messager.printMessage(kind, message);
messager.printMessage(kind, message.toString());
}
}
}

View File

@ -187,8 +187,7 @@ public class MetamodelImpl implements Metamodel {
public <X> void populate(Types.Managed<X> type) {
try {
Class<X> cls = type.getJavaType();
Class<?> mcls = J2DoPrivHelper.getForNameAction(cls.getName()+"_",
true, cls.getClassLoader()).run();
Class<?> mcls = repos.getMetaModel(cls, true);
Field[] fields = mcls.getFields();
for (Field f : fields) {
f.set(null, type.getMember(f.getName()));

View File

@ -88,10 +88,11 @@ public class SourceAnnotationHandler
* Construct with JDK6 annotation processing environment.
*
*/
public SourceAnnotationHandler(ProcessingEnvironment processingEnv) {
public SourceAnnotationHandler(ProcessingEnvironment processingEnv,
CompileTimeLogger logger) {
super();
this.processingEnv = processingEnv;
this.logger = new CompileTimeLogger(processingEnv);
this.logger = logger;
}
public int determineTypeAccess(TypeElement type) {
@ -262,7 +263,7 @@ public class SourceAnnotationHandler
}
if (!matched) {
logger.warn(_loc.get("getter-unmatched", getter,
getter.getEnclosingElement()).toString());
getter.getEnclosingElement()));
unmatched.add(getter);
}

View File

@ -37,3 +37,10 @@ getter-unmatched: Getter method "{0}" in "{1}" has no matching setter method.
mmg-tool-banner: Starting OpenJPA Annotation Processor for Metamodel Generation
mmg-process: Generating canonical metamodel source code "{0}"
mmg-tool-sign: Generated by OpenJPA MetaModel Generator Tool.
mmg-bad-source: Wrong value "{0}" of -Asource option to specify the target \
Java compiler version for the generated meta-model files. Expected a \
positive number. Setting default value as Java {1} compiler.
mmg-bad-naming: Naming policy for meta-model classes specified in -Anaming \
option "{0}" can not be used due to "{1}". Using default naming policy.
mmg-bad-log: Log level specified is -Alog option "{0}" is not recognized. \
Logging at default "{1}" level.