Skip to content
This repository was archived by the owner on Oct 22, 2020. It is now read-only.

Commit 4d4c88e

Browse files
committed
#19 - ENH: Generate META-INF/ebean-info.mf (such that we don't need ebean.mf)
1 parent e912c87 commit 4d4c88e

3 files changed

Lines changed: 218 additions & 7 deletions

File tree

src/main/java/io/ebean/querybean/generator/Constants.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,4 +27,8 @@ interface Constants {
2727
String DB = "io.ebean.DB";
2828
String FETCHGROUP = "io.ebean.FetchGroup";
2929
String QUERY = "io.ebean.Query";
30+
31+
String MODULEINFO = "io.ebean.config.ModuleInfo";
32+
String METAINF_MANIFEST = "META-INF/ebean-generated-info.mf";
33+
String METAINF_SERVICES_MODULELOADER = "META-INF/services/io.ebean.config.ModuleInfoLoader";
3034
}

src/main/java/io/ebean/querybean/generator/ProcessingContext.java

Lines changed: 196 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
package io.ebean.querybean.generator;
22

3+
import javax.annotation.processing.Filer;
4+
import javax.annotation.processing.FilerException;
35
import javax.annotation.processing.Messager;
46
import javax.annotation.processing.ProcessingEnvironment;
57
import javax.lang.model.SourceVersion;
@@ -17,34 +19,87 @@
1719
import javax.lang.model.util.Elements;
1820
import javax.lang.model.util.Types;
1921
import javax.tools.Diagnostic;
22+
import javax.tools.FileObject;
23+
import javax.tools.JavaFileObject;
24+
import java.io.FileNotFoundException;
25+
import java.io.IOException;
26+
import java.io.LineNumberReader;
27+
import java.io.Reader;
28+
import java.nio.file.NoSuchFileException;
2029
import java.util.ArrayList;
30+
import java.util.HashSet;
2131
import java.util.List;
32+
import java.util.Map;
2233
import java.util.Set;
34+
import java.util.TreeMap;
35+
import java.util.TreeSet;
36+
37+
import static javax.tools.StandardLocation.CLASS_OUTPUT;
2338

2439
/**
2540
* Context for the source generation.
2641
*/
2742
class ProcessingContext implements Constants {
2843

44+
private final ProcessingEnvironment processingEnv;
2945
private final String generatedSources;
3046

3147
private final Types typeUtils;
32-
48+
private final Filer filer;
3349
private final Messager messager;
34-
3550
private final Elements elementUtils;
36-
3751
private final String generatedAnnotation;
3852

3953
private final PropertyTypeMap propertyTypeMap = new PropertyTypeMap();
4054

55+
private final ReadModuleInfo readModuleInfo;
56+
57+
/**
58+
* All entity packages regardless of DB (for META-INF/ebean-generated-info.mf).
59+
*/
60+
private final Set<String> allEntityPackages = new TreeSet<>();
61+
62+
/**
63+
* The DB name prefixed entities.
64+
*/
65+
private final Set<String> prefixEntities = new TreeSet<>();
66+
67+
/**
68+
* Entity classes for the default database.
69+
*/
70+
private final Set<String> dbEntities = new TreeSet<>();
71+
72+
/**
73+
* Entity classes for non default databases.
74+
*/
75+
private final Map<String, Set<String>> otherDbEntities = new TreeMap<>();
76+
77+
/**
78+
* All loaded entities regardless of db (to detect ones we add back from loadedPrefixEntities).
79+
*/
80+
private final Set<String> loaded = new HashSet<>();
81+
82+
/**
83+
* For partial compile the previous list of prefixed entity classes.
84+
*/
85+
private List<String> loadedPrefixEntities;
86+
87+
/**
88+
* The package for the generated ModuleInfoLoader.
89+
*/
90+
private String factoryPackage;
91+
4192
ProcessingContext(ProcessingEnvironment processingEnv) {
93+
this.processingEnv = processingEnv;
4294
this.typeUtils = processingEnv.getTypeUtils();
95+
this.filer = processingEnv.getFiler();
4396
this.messager = processingEnv.getMessager();
4497
this.elementUtils = processingEnv.getElementUtils();
98+
4599
boolean jdk8 = processingEnv.getSourceVersion().compareTo(SourceVersion.RELEASE_8) <= 0;
46100
this.generatedAnnotation = generatedAnnotation(jdk8);
47101
this.generatedSources = initGeneratedSources(processingEnv);
102+
this.readModuleInfo = new ReadModuleInfo(this);
48103
}
49104

50105
private String generatedAnnotation(boolean jdk8) {
@@ -274,8 +329,12 @@ private String packageAppend(String origPackage) {
274329
}
275330

276331
/**
277-
* Log an error message.
332+
* Create a file writer for the given class name.
278333
*/
334+
JavaFileObject createWriter(String factoryClassName, Element originatingElement) throws IOException {
335+
return filer.createSourceFile(factoryClassName, originatingElement);
336+
}
337+
279338
void logError(Element e, String msg, Object... args) {
280339
messager.printMessage(Diagnostic.Kind.ERROR, String.format(msg, args), e);
281340
}
@@ -294,4 +353,137 @@ boolean isGeneratedAvailable() {
294353
String getGeneratedAnnotation() {
295354
return generatedAnnotation;
296355
}
356+
357+
void readModuleInfo() {
358+
String factory = loadMetaInfServices();
359+
if (factory != null) {
360+
TypeElement factoryType = elementUtils.getTypeElement(factory);
361+
if (factoryType != null) {
362+
// previous prefixed entities to add back for partial compile
363+
loadedPrefixEntities = readModuleInfo.read(factoryType);
364+
}
365+
}
366+
}
367+
368+
/**
369+
* Register an entity with optional dbName.
370+
*/
371+
void addEntity(String beanFullName, String dbName) {
372+
373+
loaded.add(beanFullName);
374+
final String pkg = packageOf(beanFullName);
375+
if (pkg != null) {
376+
allEntityPackages.add(pkg);
377+
}
378+
if (dbName != null) {
379+
prefixEntities.add(dbName + ":" + beanFullName);
380+
otherDbEntities.computeIfAbsent(dbName, s -> new TreeSet<>()).add(beanFullName);
381+
} else {
382+
prefixEntities.add(beanFullName);
383+
dbEntities.add(beanFullName);
384+
updateFactoryPackage(pkg);
385+
}
386+
}
387+
388+
/**
389+
* Add back entity classes for partial compile.
390+
*/
391+
int complete() {
392+
393+
int added = 0;
394+
if (loadedPrefixEntities != null) {
395+
for (String oldPrefixEntity : loadedPrefixEntities) {
396+
// maybe split as dbName:entityClass
397+
final String[] prefixEntityClass = oldPrefixEntity.split(":");
398+
399+
String dbName = null;
400+
String entityClass;
401+
if (prefixEntityClass.length > 1) {
402+
dbName = prefixEntityClass[0];
403+
entityClass = prefixEntityClass[1];
404+
} else {
405+
entityClass = prefixEntityClass[0];
406+
}
407+
if (!loaded.contains(entityClass)) {
408+
addEntity(entityClass, dbName);
409+
added++;
410+
}
411+
}
412+
}
413+
return added;
414+
}
415+
416+
private String packageOf(String beanFullName) {
417+
final int pos = beanFullName.lastIndexOf('.');
418+
if (pos > -1) {
419+
return beanFullName.substring(0, pos);
420+
}
421+
return null;
422+
}
423+
424+
private void updateFactoryPackage(String pkg) {
425+
if (pkg != null && (factoryPackage == null || factoryPackage.length() > pkg.length())) {
426+
factoryPackage = pkg;
427+
}
428+
}
429+
430+
FileObject createMetaInfServicesWriter() throws IOException {
431+
return createMetaInfWriter(METAINF_SERVICES_MODULELOADER);
432+
}
433+
434+
FileObject createManifestWriter() throws IOException {
435+
return createMetaInfWriter(Constants.METAINF_MANIFEST);
436+
}
437+
438+
FileObject createMetaInfWriter(String target) throws IOException {
439+
return filer.createResource(CLASS_OUTPUT, "", target);
440+
}
441+
442+
Set<String> getPrefixEntities() {
443+
return prefixEntities;
444+
}
445+
446+
Set<String> getDbEntities() {
447+
return dbEntities;
448+
}
449+
450+
Map<String, Set<String>> getOtherDbEntities() {
451+
return otherDbEntities;
452+
}
453+
454+
455+
Set<String> getAllEntityPackages() {
456+
return allEntityPackages;
457+
}
458+
459+
String getFactoryPackage() {
460+
return factoryPackage != null ? factoryPackage : "unknown";
461+
}
462+
463+
/**
464+
* Return the class name of the generated ModuleInfoLoader
465+
* (such that we can read the current meta data for partial compile).
466+
*/
467+
String loadMetaInfServices() {
468+
try {
469+
FileObject fileObject = processingEnv.getFiler().getResource(CLASS_OUTPUT, "", METAINF_SERVICES_MODULELOADER);
470+
if (fileObject != null) {
471+
Reader reader = fileObject.openReader(true);
472+
LineNumberReader lineReader = new LineNumberReader(reader);
473+
String line = lineReader.readLine();
474+
if (line != null) {
475+
return line.trim();
476+
}
477+
}
478+
479+
} catch (FileNotFoundException | NoSuchFileException e) {
480+
// ignore - no services file yet
481+
} catch (FilerException e) {
482+
logNote(null, "FilerException reading services file: " + e.getMessage());
483+
} catch (Exception e) {
484+
e.printStackTrace();
485+
logError(null, "Error reading services file: " + e.getMessage());
486+
}
487+
return null;
488+
}
297489
}

src/main/java/io/ebean/querybean/generator/Processor.java

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ public Set<String> getSupportedAnnotationTypes() {
4343
Set<String> annotations = new LinkedHashSet<>();
4444
annotations.add(ENTITY);
4545
annotations.add(EMBEDDABLE);
46+
annotations.add(MODULEINFO);
4647
return annotations;
4748
}
4849

@@ -54,28 +55,42 @@ public SourceVersion getSupportedSourceVersion() {
5455
@Override
5556
public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
5657

58+
processingContext.readModuleInfo();
5759
int count = 0;
58-
5960
for (TypeElement annotation : annotations) {
6061
for (Element element : roundEnv.getElementsAnnotatedWith(annotation)) {
6162
generateQueryBeans(element);
6263
count++;
6364
}
6465
}
66+
final int loaded = processingContext.complete();
67+
if (roundEnv.processingOver()) {
68+
writeModuleInfoBean();
69+
}
6570

6671
if (count > 0) {
67-
processingContext.logNote("Generated " + count + " query beans");
72+
String msg = "Ebean APT generated %s query beans, loaded %s others - META-INF/ebean-generated-info.mf entity-packages: %s";
73+
processingContext.logNote(msg, count, loaded, processingContext.getAllEntityPackages());
6874
}
6975

7076
return true;
7177
}
7278

79+
private void writeModuleInfoBean() {
80+
try {
81+
SimpleModuleInfoWriter writer = new SimpleModuleInfoWriter(processingContext);
82+
writer.write();
83+
} catch (Throwable e) {
84+
processingContext.logError(null, "Failed to write ModuleInfoLoader " + e.getMessage());
85+
}
86+
}
87+
7388
private void generateQueryBeans(Element element) {
7489
try {
7590
SimpleQueryBeanWriter beanWriter = new SimpleQueryBeanWriter((TypeElement) element, processingContext);
7691
beanWriter.writeRootBean();
7792
beanWriter.writeAssocBean();
78-
} catch (Exception e) {
93+
} catch (Throwable e) {
7994
processingContext.logError(element, "Error generating query beans: " + e);
8095
}
8196
}

0 commit comments

Comments
 (0)