001: /*
002: * Licensed to the Apache Software Foundation (ASF) under one
003: * or more contributor license agreements. See the NOTICE file
004: * distributed with this work for additional information
005: * regarding copyright ownership. The ASF licenses this file
006: * to you under the Apache License, Version 2.0 (the
007: * "License"); you may not use this file except in compliance
008: * with the License. You may obtain a copy of the License at
009: *
010: * http://www.apache.org/licenses/LICENSE-2.0
011: *
012: * Unless required by applicable law or agreed to in writing,
013: * software distributed under the License is distributed on an
014: * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
015: * KIND, either express or implied. See the License for the
016: * specific language governing permissions and limitations
017: * under the License.
018: */
019: package org.apache.openjpa.persistence;
020:
021: import java.io.Serializable;
022: import java.lang.annotation.Annotation;
023: import java.lang.reflect.AnnotatedElement;
024: import java.lang.reflect.Field;
025: import java.lang.reflect.Member;
026: import java.lang.reflect.Method;
027: import java.lang.reflect.Modifier;
028: import java.security.AccessController;
029: import java.util.HashMap;
030: import java.util.HashSet;
031: import java.util.Map;
032: import java.util.Set;
033: import java.util.List;
034: import java.util.ArrayList;
035: import javax.persistence.Basic;
036: import javax.persistence.Embeddable;
037: import javax.persistence.Embedded;
038: import javax.persistence.EmbeddedId;
039: import javax.persistence.ManyToMany;
040: import javax.persistence.ManyToOne;
041: import javax.persistence.OneToMany;
042: import javax.persistence.OneToOne;
043: import javax.persistence.PostLoad;
044: import javax.persistence.PostPersist;
045: import javax.persistence.PostRemove;
046: import javax.persistence.PostUpdate;
047: import javax.persistence.PrePersist;
048: import javax.persistence.PreRemove;
049: import javax.persistence.PreUpdate;
050: import javax.persistence.Transient;
051:
052: import org.apache.commons.lang.StringUtils;
053: import org.apache.openjpa.lib.util.J2DoPriv5Helper;
054: import org.apache.openjpa.lib.util.Localizer;
055: import org.apache.openjpa.lib.log.Log;
056: import org.apache.openjpa.meta.AbstractMetaDataDefaults;
057: import org.apache.openjpa.meta.ClassMetaData;
058: import org.apache.openjpa.meta.FieldMetaData;
059: import org.apache.openjpa.meta.JavaTypes;
060: import org.apache.openjpa.meta.ValueMetaData;
061: import static org.apache.openjpa.persistence.PersistenceStrategy.*;
062: import org.apache.openjpa.util.MetaDataException;
063: import org.apache.openjpa.conf.OpenJPAConfiguration;
064:
065: /**
066: * JPA-based metadata defaults.
067: *
068: * @author Patrick Linskey
069: * @author Abe White
070: * @nojavadoc
071: */
072: public class PersistenceMetaDataDefaults extends
073: AbstractMetaDataDefaults {
074:
075: private boolean _allowsMultipleMethodsForSameCallback = false;
076:
077: private static final Localizer _loc = Localizer
078: .forPackage(PersistenceMetaDataDefaults.class);
079:
080: private static final Map<Class, PersistenceStrategy> _strats = new HashMap<Class, PersistenceStrategy>();
081: private static final Set<String> _ignoredAnnos = new HashSet<String>();
082:
083: static {
084: _strats.put(Basic.class, BASIC);
085: _strats.put(ManyToOne.class, MANY_ONE);
086: _strats.put(OneToOne.class, ONE_ONE);
087: _strats.put(Embedded.class, EMBEDDED);
088: _strats.put(EmbeddedId.class, EMBEDDED);
089: _strats.put(OneToMany.class, ONE_MANY);
090: _strats.put(ManyToMany.class, MANY_MANY);
091: _strats.put(Persistent.class, PERS);
092: _strats.put(PersistentCollection.class, PERS_COLL);
093: _strats.put(PersistentMap.class, PERS_MAP);
094:
095: _ignoredAnnos.add(DetachedState.class.getName());
096: _ignoredAnnos.add(PostLoad.class.getName());
097: _ignoredAnnos.add(PostPersist.class.getName());
098: _ignoredAnnos.add(PostRemove.class.getName());
099: _ignoredAnnos.add(PostUpdate.class.getName());
100: _ignoredAnnos.add(PrePersist.class.getName());
101: _ignoredAnnos.add(PreRemove.class.getName());
102: _ignoredAnnos.add(PreUpdate.class.getName());
103: }
104:
105: public PersistenceMetaDataDefaults() {
106: setCallbackMode(CALLBACK_RETHROW | CALLBACK_ROLLBACK
107: | CALLBACK_FAIL_FAST);
108: setDataStoreObjectIdFieldUnwrapped(true);
109: }
110:
111: /**
112: * Return the code for the strategy of the given member. Return null if
113: * no strategy.
114: */
115: public static PersistenceStrategy getPersistenceStrategy(
116: FieldMetaData fmd, Member member) {
117: if (member == null)
118: return null;
119: AnnotatedElement el = (AnnotatedElement) member;
120: if (((Boolean) AccessController.doPrivileged(J2DoPriv5Helper
121: .isAnnotationPresentAction(el, Transient.class)))
122: .booleanValue())
123: return TRANSIENT;
124: if (fmd != null
125: && fmd.getManagement() != FieldMetaData.MANAGE_PERSISTENT)
126: return null;
127:
128: // look for persistence strategy in annotation table
129: PersistenceStrategy pstrat = null;
130: for (Annotation anno : el.getDeclaredAnnotations()) {
131: if (pstrat != null
132: && _strats.containsKey(anno.annotationType()))
133: throw new MetaDataException(_loc.get("already-pers",
134: member));
135: if (pstrat == null)
136: pstrat = _strats.get(anno.annotationType());
137: }
138: if (pstrat != null)
139: return pstrat;
140:
141: Class type;
142: int code;
143: if (fmd != null) {
144: type = fmd.getType();
145: code = fmd.getTypeCode();
146: } else if (member instanceof Field) {
147: type = ((Field) member).getType();
148: code = JavaTypes.getTypeCode(type);
149: } else {
150: type = ((Method) member).getReturnType();
151: code = JavaTypes.getTypeCode(type);
152: }
153:
154: switch (code) {
155: case JavaTypes.ARRAY:
156: if (type == byte[].class || type == char[].class
157: || type == Byte[].class
158: || type == Character[].class)
159: return BASIC;
160: break;
161: case JavaTypes.BOOLEAN:
162: case JavaTypes.BOOLEAN_OBJ:
163: case JavaTypes.BYTE:
164: case JavaTypes.BYTE_OBJ:
165: case JavaTypes.CHAR:
166: case JavaTypes.CHAR_OBJ:
167: case JavaTypes.DOUBLE:
168: case JavaTypes.DOUBLE_OBJ:
169: case JavaTypes.FLOAT:
170: case JavaTypes.FLOAT_OBJ:
171: case JavaTypes.INT:
172: case JavaTypes.INT_OBJ:
173: case JavaTypes.LONG:
174: case JavaTypes.LONG_OBJ:
175: case JavaTypes.SHORT:
176: case JavaTypes.SHORT_OBJ:
177: case JavaTypes.STRING:
178: case JavaTypes.BIGDECIMAL:
179: case JavaTypes.BIGINTEGER:
180: case JavaTypes.DATE:
181: return BASIC;
182: case JavaTypes.OBJECT:
183: if (Enum.class.isAssignableFrom(type))
184: return BASIC;
185: break;
186: }
187:
188: //### EJB3: what if defined in XML?
189: if (((Boolean) AccessController.doPrivileged(J2DoPriv5Helper
190: .isAnnotationPresentAction(type, Embeddable.class)))
191: .booleanValue())
192: return EMBEDDED;
193: if (Serializable.class.isAssignableFrom(type))
194: return BASIC;
195: return null;
196: }
197:
198: /**
199: * Flags if multiple methods of the same class can handle the same
200: * callback event.
201: */
202: public boolean getAllowsMultipleMethodsForSameCallback() {
203: return _allowsMultipleMethodsForSameCallback;
204: }
205:
206: /**
207: * Flags if multiple methods of the same class can handle the same
208: * callback event.
209: */
210: public void setAllowsMultipleMethodsForSameCallback(boolean flag) {
211: _allowsMultipleMethodsForSameCallback = flag;
212: }
213:
214: /**
215: * Auto-configuration method for the default access type of base classes
216: * with ACCESS_UNKNOWN
217: */
218: public void setDefaultAccessType(String type) {
219: if (type == null)
220: return;
221: if ("PROPERTY".equals(type.toUpperCase()))
222: setDefaultAccessType(ClassMetaData.ACCESS_PROPERTY);
223: else
224: setDefaultAccessType(ClassMetaData.ACCESS_FIELD);
225: }
226:
227: @Override
228: public void populate(ClassMetaData meta, int access) {
229: super .populate(meta, access);
230: meta.setDetachable(true);
231: // do not call get*Fields as it will lock down the fields.
232: }
233:
234: @Override
235: protected void populate(FieldMetaData fmd) {
236: setCascadeNone(fmd);
237: setCascadeNone(fmd.getKey());
238: setCascadeNone(fmd.getElement());
239: }
240:
241: /**
242: * Turns off auto cascading of persist, refresh, attach.
243: */
244: static void setCascadeNone(ValueMetaData vmd) {
245: vmd.setCascadePersist(ValueMetaData.CASCADE_NONE);
246: vmd.setCascadeRefresh(ValueMetaData.CASCADE_NONE);
247: vmd.setCascadeAttach(ValueMetaData.CASCADE_NONE);
248: }
249:
250: @Override
251: protected int getAccessType(ClassMetaData meta) {
252: return getAccessType(meta.getDescribedType());
253: }
254:
255: /**
256: * Recursive helper to determine access type based on annotation placement.
257: */
258: private int getAccessType(Class cls) {
259: // traversed entire hierarchy without finding annotations
260: if (cls == null || cls == Object.class)
261: return ClassMetaData.ACCESS_UNKNOWN;
262:
263: int access = 0;
264: if (annotated(
265: (Field[]) AccessController.doPrivileged(J2DoPriv5Helper
266: .getDeclaredFieldsAction(cls))).size() > 0)
267: access |= ClassMetaData.ACCESS_FIELD;
268: if (annotated(
269: (Method[]) AccessController
270: .doPrivileged(J2DoPriv5Helper
271: .getDeclaredMethodsAction(cls))).size() > 0
272: || cls.isInterface()) // OpenJPA managed ifaces must use prop access
273: access |= ClassMetaData.ACCESS_PROPERTY;
274: return getAccessType(cls.getSuperclass()) | access;
275: }
276:
277: @Override
278: protected List getFieldAccessNames(ClassMetaData meta) {
279: return annotated((Field[]) AccessController
280: .doPrivileged(J2DoPriv5Helper
281: .getDeclaredFieldsAction(meta
282: .getDescribedType())));
283: }
284:
285: @Override
286: protected List getPropertyAccessNames(ClassMetaData meta) {
287: return annotated((Method[]) AccessController
288: .doPrivileged(J2DoPriv5Helper
289: .getDeclaredMethodsAction(meta
290: .getDescribedType())));
291: }
292:
293: /**
294: * Return the members of <code>members</code> that have persistence
295: * annotations.
296: */
297: private static List annotated(AnnotatedElement[] members) {
298: Annotation[] annos;
299: String name;
300: List annotated = new ArrayList(members.length);
301: for (int i = 0; i < members.length; i++) {
302: annos = (Annotation[]) AccessController
303: .doPrivileged(J2DoPriv5Helper
304: .getAnnotationsAction(members[i]));
305: for (int j = 0; j < annos.length; j++) {
306: name = annos[j].annotationType().getName();
307: if ((name.startsWith("javax.persistence.") || name
308: .startsWith("org.apache.openjpa.persistence."))
309: && !_ignoredAnnos.contains(name))
310: annotated.add(members[i]);
311: }
312: }
313: return annotated;
314: }
315:
316: protected boolean isDefaultPersistent(ClassMetaData meta,
317: Member member, String name) {
318: int mods = member.getModifiers();
319: if (Modifier.isTransient(mods))
320: return false;
321:
322: if (member instanceof Method) {
323: try {
324: // check for setters for methods
325: Method setter = (Method) AccessController
326: .doPrivileged(J2DoPriv5Helper
327: .getDeclaredMethodAction(meta
328: .getDescribedType(), "set"
329: + StringUtils.capitalize(name),
330: new Class[] { ((Method) member)
331: .getReturnType() }));
332: if (setter == null && !isAnnotatedTransient(member)) {
333: logNoSetter(meta, name, null);
334: return false;
335: }
336: } catch (Exception e) {
337: // e.g., NoSuchMethodException
338: if (!isAnnotatedTransient(member))
339: logNoSetter(meta, name, e);
340: return false;
341: }
342: }
343:
344: PersistenceStrategy strat = getPersistenceStrategy(null, member);
345: if (strat == null || strat == PersistenceStrategy.TRANSIENT)
346: return false;
347: return true;
348: }
349:
350: private boolean isAnnotatedTransient(Member member) {
351: return member instanceof AnnotatedElement
352: && ((Boolean) AccessController
353: .doPrivileged(J2DoPriv5Helper
354: .isAnnotationPresentAction(
355: ((AnnotatedElement) member),
356: Transient.class)))
357: .booleanValue();
358: }
359:
360: private void logNoSetter(ClassMetaData meta, String name,
361: Exception e) {
362: Log log = meta.getRepository().getConfiguration().getLog(
363: OpenJPAConfiguration.LOG_METADATA);
364: if (log.isWarnEnabled())
365: log.warn(_loc.get("no-setter-for-getter", name, meta
366: .getDescribedType().getName()));
367: else if (log.isTraceEnabled())
368: // log the exception, if any, if we're in trace-level debugging
369: log.warn(_loc.get("no-setter-for-getter", name, meta
370: .getDescribedType().getName()), e);
371: }
372: }
|