001: /*
002: * Copyright (c) 1998 - 2005 Versant Corporation
003: * All rights reserved. This program and the accompanying materials
004: * are made available under the terms of the Eclipse Public License v1.0
005: * which accompanies this distribution, and is available at
006: * http://www.eclipse.org/legal/epl-v10.html
007: *
008: * Contributors:
009: * Versant Corporation - initial API and implementation
010: */
011: package com.versant.core.metadata;
012:
013: import com.versant.core.metadata.parser.*;
014: import com.versant.core.util.classhelper.ClassHelper;
015: import com.versant.core.common.BindingSupportImpl;
016:
017: import javax.persistence.*;
018: import java.lang.reflect.*;
019: import java.lang.annotation.Annotation;
020: import java.util.Map;
021: import java.util.HashMap;
022: import java.io.Serializable;
023:
024: /**
025: * MetaData PreProcessor for ejb jdbc metadata. The preprocessor looks for annotations
026: * and set the equivalent jdo metadata. If no annotations is present then the ejb defaults
027: * will be applied.
028: */
029: public class EJBAnnotationProcessor implements JdoExtensionKeys,
030: MetaDataPreProcessor {
031: private ClassLoader loader;
032: private MetaDataUtils mdutils;
033: private final Class[] EMPTY_CLASS_ARRAY;
034:
035: public EJBAnnotationProcessor(ClassLoader loader,
036: MetaDataUtils mdutils) {
037: this .loader = loader;
038: this .mdutils = mdutils;
039: EMPTY_CLASS_ARRAY = new Class[] {};
040: }
041:
042: public void process(JdoClass jdoClass) {
043: Class cls;
044: try {
045: cls = loadClass(jdoClass.getQName());
046: } catch (ClassNotFoundException e) {
047: throw new RuntimeException(e);
048: }
049:
050: if (!cls.isAnnotationPresent(javax.persistence.Entity.class))
051: return;
052: javax.persistence.Entity ea = (Entity) cls
053: .getAnnotation(javax.persistence.Entity.class);
054:
055: //change to appid
056: jdoClass.identityType = MDStatics.IDENTITY_TYPE_APPLICATION;
057: jdoClass.objectIdClasssRequired = false;
058:
059: //update table name
060: if (cls.isAnnotationPresent(javax.persistence.Table.class)) {
061: javax.persistence.Table ta = (Table) cls
062: .getAnnotation(javax.persistence.Table.class);
063: JdoExtension ext = JdoExtension.find(JDBC_TABLE_NAME,
064: jdoClass.elements);
065: if (ext == null) {
066: ext = new JdoExtension();
067: ext.key = JDBC_TABLE_NAME;
068: ext.value = ta.name();
069: jdoClass.addElement(ext);
070: }
071: }
072:
073: //calculate pc super
074: doPCSuperClass(jdoClass, cls);
075:
076: //create a map for jdoField name to jdoField instance.
077: Map jdoFieldMap = createFieldMap(jdoClass);
078:
079: final AccessibleMember accessibleMember = new AccessibleMember();
080: if (ea.access() == AccessType.FIELD) {
081: Field[] fields = cls.getDeclaredFields();
082: for (int i = 0; i < fields.length; i++) {
083: accessibleMember.init(fields[i], null);
084: bla(jdoFieldMap, jdoClass, accessibleMember);
085: }
086: } else {
087: Field[] fields = cls.getDeclaredFields();
088: for (int i = 0; i < fields.length; i++) {
089: String s = fields[i].getName();
090: char[] ca = s.toCharArray();
091: ca[0] = Character.toUpperCase(ca[0]);
092: Method m = null;
093: try {
094: m = cls.getDeclaredMethod("get"
095: + String.valueOf(ca), EMPTY_CLASS_ARRAY);
096: } catch (NoSuchMethodException e) {
097: // System.out.println("method 'get" + String.valueOf(ca) + "' not found");
098: try {
099: m = cls
100: .getDeclaredMethod("is"
101: + String.valueOf(ca),
102: EMPTY_CLASS_ARRAY);
103: // System.out.println("method 'is" + String.valueOf(ca) + "' not found");
104: } catch (NoSuchMethodException e1) {
105: }
106: }
107: //only proceed if the method is found.
108: if (m == null) {
109: // System.out.println("Accessor method not found for field '" + fields[i].getName() + "'");
110: continue;
111: }
112: accessibleMember.init(fields[i], m);
113: bla(jdoFieldMap, jdoClass, accessibleMember);
114: }
115: }
116: }
117:
118: private void bla(Map jdoFieldMap, JdoClass jdoClass,
119: AccessibleMember property) {
120: //ignore certain fields
121: if (property.isSynthetic())
122: return;
123: if (Modifier.isStatic(property.getModifiers()))
124: return;
125: if (mdutils.isEnhancerAddedField(property.getName()))
126: return;
127: //check if there is a field for the property
128:
129: JdoField jdoField = getJdoField(jdoFieldMap, property, jdoClass);
130:
131: if (jdoField.persistenceModifier == 0
132: && property.isAnnotationPresent(Transient.class)) {
133: jdoField.persistenceModifier = MDStatics.PERSISTENCE_MODIFIER_NONE;
134: return;
135: }
136:
137: processId(property, jdoField, jdoClass);
138:
139: if (property
140: .isAnnotationPresent(javax.persistence.Column.class)) {
141: javax.persistence.Column ca = property
142: .getAnnotation(javax.persistence.Column.class);
143: if (isValid(ca.name())) {
144: jdoField.findCreate(JDBC_COLUMN, null, false)
145: .findCreate(JDBC_COLUMN_NAME, ca.name(), false);
146: }
147: if (ca.scale() != 0) {
148: jdoField.findCreate(JDBC_COLUMN, null, false)
149: .findCreate(JDBC_SCALE, "" + ca.scale(), false);
150: }
151: if (ca.length() != 255) {
152: jdoField.findCreate(JDBC_COLUMN, null, false)
153: .findCreate(JDBC_LENGTH, "" + ca.length(),
154: false);
155: }
156:
157: if (ca.primaryKey())
158: jdoField.primaryKey = true;
159: }
160:
161: if (property.isAnnotationPresent(Basic.class)) {
162: Basic basic = property.getAnnotation(Basic.class);
163: jdoField.defaultFetchGroup = (basic.fetch() == FetchType.EAGER) ? MDStatics.TRUE
164: : MDStatics.FALSE;
165: } else if (property.isAnnotationPresent(OneToOne.class)) {
166: OneToOne oto = property.getAnnotation(OneToOne.class);
167: if (oto.cascade() != null) {
168: setCascadeType(oto.cascade(), jdoField);
169: }
170: if (!oto.optional())
171: jdoField.nullValue = MDStatics.NULL_VALUE_EXCEPTION;
172: jdoField.defaultFetchGroup = (oto.fetch() == FetchType.EAGER) ? MDStatics.TRUE
173: : MDStatics.FALSE;
174: } else if (property.isAnnotationPresent(OneToMany.class)) {
175: OneToMany oto = property.getAnnotation(OneToMany.class);
176: if (oto.cascade() != null) {
177: setCascadeType(oto.cascade(), jdoField);
178: }
179: jdoField.defaultFetchGroup = (oto.fetch() == FetchType.EAGER) ? MDStatics.TRUE
180: : MDStatics.FALSE;
181:
182: JdoCollection col = jdoField.collection;
183: if (col == null) {
184: col = jdoField.collection = new JdoCollection();
185: jdoField.collection.parent = jdoField;
186: }
187:
188: if (!isValid(col.elementType)
189: && isValid(oto.targetEntity())) {
190: col.elementType = oto.targetEntity();
191: }
192:
193: //inverse field
194: if (isValid(oto.mappedBy())) {
195: col.findCreate(INVERSE, oto.mappedBy(), false);
196: }
197: } else if (property.isAnnotationPresent(ManyToMany.class)) {
198: ManyToMany oto = property.getAnnotation(ManyToMany.class);
199: if (oto.cascade() != null) {
200: setCascadeType(oto.cascade(), jdoField);
201: }
202: jdoField.defaultFetchGroup = (oto.fetch() == FetchType.EAGER) ? MDStatics.TRUE
203: : MDStatics.FALSE;
204:
205: } else if (property.isAnnotationPresent(ManyToOne.class)) {
206: ManyToOne oto = property.getAnnotation(ManyToOne.class);
207: if (oto.cascade() != null) {
208: setCascadeType(oto.cascade(), jdoField);
209: }
210: jdoField.defaultFetchGroup = (oto.fetch() == FetchType.EAGER) ? MDStatics.TRUE
211: : MDStatics.FALSE;
212:
213: } else if (property.isAnnotationPresent(Serialized.class)) {
214: Serialized oto = property.getAnnotation(Serialized.class);
215: jdoField.defaultFetchGroup = (oto.fetch() == FetchType.EAGER) ? MDStatics.TRUE
216: : MDStatics.FALSE;
217: jdoField.findCreate(EXTERNALIZER, "SERIALIZED", false);
218: } else if (property.isAnnotationPresent(Embedded.class)) {
219: jdoField.embedded = MDStatics.TRUE;
220: jdoField.defaultFetchGroup = MDStatics.FALSE;
221: } else if (property.isAnnotationPresent(Lob.class)) {
222: Lob lob = property.getAnnotation(Lob.class);
223: jdoField.defaultFetchGroup = (lob.fetch() == FetchType.EAGER) ? MDStatics.TRUE
224: : MDStatics.FALSE;
225: } else if (property.isAnnotationPresent(Version.class)) {
226: jdoField.findCreate(JDBC_OPTIMISTIC_LOCKING, "version",
227: false);
228: } else {
229: if (!mdutils.isPersistentModifiers(property.getModifiers())) {
230: jdoField.persistenceModifier = MDStatics.PERSISTENCE_MODIFIER_NONE;
231: } else if (mdutils.isBasicType(property.getType())) {
232: jdoField.defaultFetchGroup = MDStatics.TRUE;
233: } else if (property.getType().isAnnotationPresent(
234: javax.persistence.Embeddable.class)) {
235: jdoField.embedded = MDStatics.TRUE;
236: jdoField.defaultFetchGroup = MDStatics.TRUE;
237: } else if (Serializable.class.isAssignableFrom(property
238: .getType())) {
239: jdoField.findCreate(EXTERNALIZER, "SERIALIZED", false);
240: jdoField.defaultFetchGroup = MDStatics.TRUE;
241: } else if (java.sql.Blob.class.equals(property.getType())
242: || java.sql.Clob.class.equals(property.getType())) {
243: jdoField.defaultFetchGroup = MDStatics.FALSE;
244: }
245: }
246: }
247:
248: private void processId(AccessibleMember property,
249: JdoField jdoField, JdoClass jdoClass) {
250: /**
251: * TODO
252: * composite pk?
253: *
254: */
255: if (property.isAnnotationPresent(Id.class)) {
256: jdoField.primaryKey = true;
257:
258: Id idAno = (Id) property.getAnnotation(Id.class);
259: //the current keygenExt as per jdo metadata
260: JdoExtension keygenExt = JdoExtension.find(
261: JDBC_KEY_GENERATOR, jdoClass.elements);
262:
263: String generator = idAno.generator();
264: if (idAno.generate() == GeneratorType.NONE) {
265: if (keygenExt != null) {
266: //removing it by setting the key to -1
267: keygenExt.key = -1;
268: }
269: } else if (idAno.generate() == GeneratorType.SEQUENCE
270: || idAno.generate() == GeneratorType.IDENTITY) {
271: keygenExt = createKeyGen(jdoClass, keygenExt);
272: keygenExt.value = "AUTOINC";
273: if (generator != null && generator.length() > 0) {
274: keygenExt.value = generator;
275: }
276: } else if (idAno.generate() == GeneratorType.AUTO) {
277: throw new RuntimeException("NotImplemented");
278: } else if (idAno.generate() == GeneratorType.TABLE) {
279: keygenExt = createKeyGen(jdoClass, keygenExt);
280: if (generator != null && generator.length() > 0) {
281: keygenExt.value = generator;
282: } else {
283: throw new RuntimeException("Must ' '"
284: + idAno.generate() + "'");
285: }
286: } else {
287: throw new RuntimeException(
288: "Unsupported GeneratorType '"
289: + idAno.generate() + "'");
290: }
291: }
292: }
293:
294: private JdoField getJdoField(Map jdoFieldMap, Member field,
295: JdoClass jdoClass) {
296: JdoField jdoField = (JdoField) jdoFieldMap.get(field.getName());
297: if (jdoField == null) {
298: jdoField = new JdoField();
299: jdoField.name = field.getName();
300: jdoField.parent = jdoClass;
301: jdoClass.addElement(jdoField);
302: jdoFieldMap.put(jdoField.name, jdoField);
303: }
304: return jdoField;
305: }
306:
307: private Map createFieldMap(JdoClass jdoClass) {
308: Map jdoFieldMap = new HashMap();
309: if (jdoClass.elements != null) {
310: JdoElement[] elements = jdoClass.elements;
311: for (int i = 0; i < elements.length; i++) {
312: JdoElement element = elements[i];
313: if (element instanceof JdoField) {
314: jdoFieldMap.put(((JdoField) element).name, element);
315: }
316: }
317: }
318: return jdoFieldMap;
319: }
320:
321: private boolean isValid(String value) {
322: if (value == null || value.length() == 0)
323: return false;
324: return true;
325: }
326:
327: private void setCascadeType(CascadeType[] cct, JdoField jdoField) {
328: for (int j = 0; j < cct.length; j++) {
329: CascadeType cascadeType = cct[j];
330: switch (cascadeType) {
331: case ALL:
332: jdoField.cascadeType = MDStatics.CASCADE_ALL;
333: break;
334: case MERGE:
335: jdoField.cascadeType += MDStatics.CASCADE_MERGE;
336: break;
337: case PERSIST:
338: jdoField.cascadeType += MDStatics.CASCADE_PERSIST;
339: break;
340: case REFRESH:
341: jdoField.cascadeType += MDStatics.CASCADE_REFRESH;
342: break;
343: case REMOVE:
344: jdoField.cascadeType += MDStatics.CASCADE_REMOVE;
345: break;
346: default:
347: throw BindingSupportImpl.getInstance().internal(
348: "unhandled CascadeType '" + cascadeType + "'");
349: }
350: }
351: }
352:
353: private JdoExtension createKeyGen(JdoClass jdoCls,
354: JdoExtension keygenExt) {
355: if (keygenExt == null) {
356: keygenExt = new JdoExtension();
357: keygenExt.key = JDBC_KEY_GENERATOR;
358: jdoCls.addElement(keygenExt);
359: }
360: return keygenExt;
361: }
362:
363: private void doPCSuperClass(JdoClass jdoClass, Class subClass) {
364: Class super Class = subClass.getSuperclass();
365: if (super Class == null)
366: return;
367:
368: String super ClassName = jdoClass.getPCSuperClassQName();
369: if (super ClassName == null
370: && super Class
371: .isAnnotationPresent(javax.persistence.Entity.class)) {
372: jdoClass.pcSuperclass = super Class.getName();
373: }
374: }
375:
376: /**
377: * Load class name using our loader.
378: */
379: private Class loadClass(String name) throws ClassNotFoundException {
380: return ClassHelper.get().classForName(name, false, loader);
381: }
382:
383: /**
384: * Structure to provide info for a field and/or method.
385: */
386: private class AccessibleMember extends AccessibleObject implements
387: Member {
388: private Field field;
389: private Method method;
390:
391: public void init(Field field, Method method) {
392: this .field = field;
393: this .method = method;
394: }
395:
396: public Class getDeclaringClass() {
397: return field.getDeclaringClass();
398: }
399:
400: public int getModifiers() {
401: return field.getModifiers();
402: }
403:
404: public String getName() {
405: return field.getName();
406: }
407:
408: public boolean isSynthetic() {
409: return field.isSynthetic();
410: }
411:
412: public <T extends Annotation> T getAnnotation(
413: Class<T> annotationClass) {
414: if (method == null) {
415: return field.getAnnotation(annotationClass);
416: } else {
417: return method.getAnnotation(annotationClass);
418: }
419: }
420:
421: public Annotation[] getAnnotations() {
422: if (method == null) {
423: return field.getAnnotations();
424: } else {
425: return method.getAnnotations();
426: }
427: }
428:
429: public Annotation[] getDeclaredAnnotations() {
430: if (method == null) {
431: return field.getDeclaredAnnotations();
432: } else {
433: return method.getDeclaredAnnotations();
434: }
435: }
436:
437: public boolean isAccessible() {
438: if (method == null) {
439: return field.isAccessible();
440: } else {
441: return method.isAccessible();
442: }
443: }
444:
445: public boolean isAnnotationPresent(
446: Class<? extends Annotation> annotationClass) {
447: if (method == null) {
448: return field.isAnnotationPresent(annotationClass);
449: } else {
450: return method.isAnnotationPresent(annotationClass);
451: }
452: }
453:
454: public void setAccessible(boolean flag)
455: throws SecurityException {
456: if (method == null) {
457: field.setAccessible(flag);
458: } else {
459: method.setAccessible(flag);
460: }
461: }
462:
463: public Class getType() {
464: return field.getType();
465: }
466:
467: }
468: }
|