| /* |
| * 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; |
| |
| import static javax.persistence.GenerationType.AUTO; |
| import static org.apache.openjpa.persistence.MetaDataTag.ACCESS; |
| import static org.apache.openjpa.persistence.MetaDataTag.CACHEABLE; |
| import static org.apache.openjpa.persistence.MetaDataTag.DATASTORE_ID; |
| import static org.apache.openjpa.persistence.MetaDataTag.DATA_CACHE; |
| import static org.apache.openjpa.persistence.MetaDataTag.DEPENDENT; |
| import static org.apache.openjpa.persistence.MetaDataTag.DETACHED_STATE; |
| import static org.apache.openjpa.persistence.MetaDataTag.ELEM_DEPENDENT; |
| import static org.apache.openjpa.persistence.MetaDataTag.ELEM_TYPE; |
| import static org.apache.openjpa.persistence.MetaDataTag.EMBEDDED_ID; |
| import static org.apache.openjpa.persistence.MetaDataTag.ENTITY_LISTENERS; |
| import static org.apache.openjpa.persistence.MetaDataTag.EXCLUDE_DEFAULT_LISTENERS; |
| import static org.apache.openjpa.persistence.MetaDataTag.EXCLUDE_SUPERCLASS_LISTENERS; |
| import static org.apache.openjpa.persistence.MetaDataTag.EXTERNALIZER; |
| import static org.apache.openjpa.persistence.MetaDataTag.EXTERNAL_VALS; |
| import static org.apache.openjpa.persistence.MetaDataTag.FACTORY; |
| import static org.apache.openjpa.persistence.MetaDataTag.FETCH_GROUP; |
| import static org.apache.openjpa.persistence.MetaDataTag.FETCH_GROUPS; |
| import static org.apache.openjpa.persistence.MetaDataTag.FLUSH_MODE; |
| import static org.apache.openjpa.persistence.MetaDataTag.GENERATED_VALUE; |
| import static org.apache.openjpa.persistence.MetaDataTag.ID; |
| import static org.apache.openjpa.persistence.MetaDataTag.ID_CLASS; |
| import static org.apache.openjpa.persistence.MetaDataTag.INVERSE_LOGICAL; |
| import static org.apache.openjpa.persistence.MetaDataTag.KEY_DEPENDENT; |
| import static org.apache.openjpa.persistence.MetaDataTag.KEY_TYPE; |
| import static org.apache.openjpa.persistence.MetaDataTag.LOAD_FETCH_GROUP; |
| import static org.apache.openjpa.persistence.MetaDataTag.LRS; |
| import static org.apache.openjpa.persistence.MetaDataTag.MANAGED_INTERFACE; |
| import static org.apache.openjpa.persistence.MetaDataTag.MAPPED_BY_ID; |
| import static org.apache.openjpa.persistence.MetaDataTag.MAP_KEY; |
| import static org.apache.openjpa.persistence.MetaDataTag.MAP_KEY_CLASS; |
| import static org.apache.openjpa.persistence.MetaDataTag.NATIVE_QUERIES; |
| import static org.apache.openjpa.persistence.MetaDataTag.NATIVE_QUERY; |
| import static org.apache.openjpa.persistence.MetaDataTag.ORDER_BY; |
| import static org.apache.openjpa.persistence.MetaDataTag.POST_LOAD; |
| import static org.apache.openjpa.persistence.MetaDataTag.POST_PERSIST; |
| import static org.apache.openjpa.persistence.MetaDataTag.POST_REMOVE; |
| import static org.apache.openjpa.persistence.MetaDataTag.POST_UPDATE; |
| import static org.apache.openjpa.persistence.MetaDataTag.PRE_PERSIST; |
| import static org.apache.openjpa.persistence.MetaDataTag.PRE_REMOVE; |
| import static org.apache.openjpa.persistence.MetaDataTag.PRE_UPDATE; |
| import static org.apache.openjpa.persistence.MetaDataTag.QUERIES; |
| import static org.apache.openjpa.persistence.MetaDataTag.QUERY; |
| import static org.apache.openjpa.persistence.MetaDataTag.READ_ONLY; |
| import static org.apache.openjpa.persistence.MetaDataTag.SEQ_GENERATOR; |
| import static org.apache.openjpa.persistence.MetaDataTag.STOREDPROCEDURE_QUERIES; |
| import static org.apache.openjpa.persistence.MetaDataTag.STOREDPROCEDURE_QUERY; |
| import static org.apache.openjpa.persistence.MetaDataTag.TYPE; |
| import static org.apache.openjpa.persistence.MetaDataTag.VERSION; |
| |
| import java.io.File; |
| import java.io.Serializable; |
| import java.lang.annotation.Annotation; |
| import java.lang.reflect.AnnotatedElement; |
| import java.lang.reflect.Field; |
| import java.lang.reflect.Member; |
| import java.lang.reflect.Method; |
| import java.lang.reflect.Modifier; |
| import java.net.URISyntaxException; |
| import java.net.URL; |
| import java.security.AccessController; |
| import java.util.ArrayList; |
| import java.util.Arrays; |
| import java.util.Collection; |
| import java.util.Comparator; |
| import java.util.HashMap; |
| import java.util.HashSet; |
| import java.util.Map; |
| import java.util.Properties; |
| import java.util.Set; |
| import java.util.Stack; |
| import java.util.TreeSet; |
| |
| import javax.persistence.Access; |
| import javax.persistence.AccessType; |
| import javax.persistence.Basic; |
| import javax.persistence.Cacheable; |
| import javax.persistence.CascadeType; |
| import javax.persistence.ElementCollection; |
| import javax.persistence.Embeddable; |
| import javax.persistence.Embedded; |
| import javax.persistence.EmbeddedId; |
| import javax.persistence.Entity; |
| import javax.persistence.EntityListeners; |
| import javax.persistence.ExcludeDefaultListeners; |
| import javax.persistence.ExcludeSuperclassListeners; |
| import javax.persistence.FetchType; |
| import javax.persistence.FlushModeType; |
| import javax.persistence.GeneratedValue; |
| import javax.persistence.GenerationType; |
| import javax.persistence.Id; |
| import javax.persistence.IdClass; |
| import javax.persistence.Lob; |
| import javax.persistence.LockModeType; |
| import javax.persistence.ManyToMany; |
| import javax.persistence.ManyToOne; |
| import javax.persistence.MapKey; |
| import javax.persistence.MapKeyClass; |
| import javax.persistence.MappedSuperclass; |
| import javax.persistence.MapsId; |
| import javax.persistence.NamedNativeQueries; |
| import javax.persistence.NamedNativeQuery; |
| import javax.persistence.NamedQueries; |
| import javax.persistence.NamedQuery; |
| import javax.persistence.NamedStoredProcedureQueries; |
| import javax.persistence.NamedStoredProcedureQuery; |
| import javax.persistence.OneToMany; |
| import javax.persistence.OneToOne; |
| import javax.persistence.OrderBy; |
| import javax.persistence.ParameterMode; |
| 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 javax.persistence.QueryHint; |
| import javax.persistence.SequenceGenerator; |
| import javax.persistence.StoredProcedureParameter; |
| import javax.persistence.Version; |
| |
| import org.apache.openjpa.conf.OpenJPAConfiguration; |
| import org.apache.openjpa.event.BeanLifecycleCallbacks; |
| import org.apache.openjpa.event.LifecycleCallbacks; |
| import org.apache.openjpa.event.LifecycleEvent; |
| import org.apache.openjpa.event.MethodLifecycleCallbacks; |
| import org.apache.openjpa.kernel.QueryLanguages; |
| import org.apache.openjpa.kernel.jpql.JPQLParser; |
| import org.apache.openjpa.lib.conf.Configurations; |
| import org.apache.openjpa.lib.log.Log; |
| import org.apache.openjpa.lib.meta.SourceTracker; |
| import org.apache.openjpa.lib.util.J2DoPrivHelper; |
| import org.apache.openjpa.lib.util.Localizer; |
| import org.apache.openjpa.lib.util.StringUtil; |
| import org.apache.openjpa.meta.AccessCode; |
| import org.apache.openjpa.meta.ClassMetaData; |
| import org.apache.openjpa.meta.DelegatingMetaDataFactory; |
| import org.apache.openjpa.meta.FieldMetaData; |
| import org.apache.openjpa.meta.JavaTypes; |
| import org.apache.openjpa.meta.LifecycleMetaData; |
| import org.apache.openjpa.meta.MetaDataFactory; |
| import org.apache.openjpa.meta.MetaDataModes; |
| import org.apache.openjpa.meta.MetaDataRepository; |
| import org.apache.openjpa.meta.MultiQueryMetaData; |
| import org.apache.openjpa.meta.Order; |
| import org.apache.openjpa.meta.QueryMetaData; |
| import org.apache.openjpa.meta.SequenceMetaData; |
| import org.apache.openjpa.meta.UpdateStrategies; |
| import org.apache.openjpa.meta.ValueMetaData; |
| import org.apache.openjpa.meta.ValueStrategies; |
| import org.apache.openjpa.util.ImplHelper; |
| import org.apache.openjpa.util.InternalException; |
| import org.apache.openjpa.util.MetaDataException; |
| import org.apache.openjpa.util.UnsupportedException; |
| import org.apache.openjpa.util.UserException; |
| |
| |
| /** |
| * Persistence annotation metadata parser. Currently does not parse |
| * deployment descriptors. |
| * |
| * @author Abe White |
| * @author Steve Kim |
| */ |
| public class AnnotationPersistenceMetaDataParser |
| implements MetaDataModes { |
| |
| private static final Localizer _loc = Localizer.forPackage |
| (AnnotationPersistenceMetaDataParser.class); |
| |
| private static final Map<Class<?>, MetaDataTag> _tags = |
| new HashMap<>(); |
| |
| static { |
| _tags.put(Access.class, ACCESS); |
| _tags.put(Cacheable.class, CACHEABLE); |
| _tags.put(EmbeddedId.class, EMBEDDED_ID); |
| _tags.put(EntityListeners.class, ENTITY_LISTENERS); |
| _tags.put(ExcludeDefaultListeners.class, EXCLUDE_DEFAULT_LISTENERS); |
| _tags.put(ExcludeSuperclassListeners.class, |
| EXCLUDE_SUPERCLASS_LISTENERS); |
| _tags.put(FlushModeType.class, FLUSH_MODE); |
| _tags.put(GeneratedValue.class, GENERATED_VALUE); |
| _tags.put(Id.class, ID); |
| _tags.put(IdClass.class, ID_CLASS); |
| _tags.put(MapKey.class, MAP_KEY); |
| _tags.put(MapKeyClass.class, MAP_KEY_CLASS); |
| _tags.put(MapsId.class, MAPPED_BY_ID); |
| _tags.put(NamedNativeQueries.class, NATIVE_QUERIES); |
| _tags.put(NamedNativeQuery.class, NATIVE_QUERY); |
| _tags.put(NamedStoredProcedureQueries.class, STOREDPROCEDURE_QUERIES); |
| _tags.put(NamedStoredProcedureQuery.class, STOREDPROCEDURE_QUERY); |
| _tags.put(NamedQueries.class, QUERIES); |
| _tags.put(NamedQuery.class, QUERY); |
| _tags.put(OrderBy.class, ORDER_BY); |
| _tags.put(PostLoad.class, POST_LOAD); |
| _tags.put(PostPersist.class, POST_PERSIST); |
| _tags.put(PostRemove.class, POST_REMOVE); |
| _tags.put(PostUpdate.class, POST_UPDATE); |
| _tags.put(PrePersist.class, PRE_PERSIST); |
| _tags.put(PreRemove.class, PRE_REMOVE); |
| _tags.put(PreUpdate.class, PRE_UPDATE); |
| _tags.put(SequenceGenerator.class, SEQ_GENERATOR); |
| _tags.put(Version.class, VERSION); |
| _tags.put(DataCache.class, DATA_CACHE); |
| _tags.put(DataStoreId.class, DATASTORE_ID); |
| _tags.put(Dependent.class, DEPENDENT); |
| _tags.put(DetachedState.class, DETACHED_STATE); |
| _tags.put(ElementDependent.class, ELEM_DEPENDENT); |
| _tags.put(ElementType.class, ELEM_TYPE); |
| _tags.put(ExternalValues.class, EXTERNAL_VALS); |
| _tags.put(Externalizer.class, EXTERNALIZER); |
| _tags.put(Factory.class, FACTORY); |
| _tags.put(FetchGroup.class, FETCH_GROUP); |
| _tags.put(FetchGroups.class, FETCH_GROUPS); |
| _tags.put(InverseLogical.class, INVERSE_LOGICAL); |
| _tags.put(KeyDependent.class, KEY_DEPENDENT); |
| _tags.put(KeyType.class, KEY_TYPE); |
| _tags.put(LoadFetchGroup.class, LOAD_FETCH_GROUP); |
| _tags.put(LRS.class, LRS); |
| _tags.put(ManagedInterface.class, MANAGED_INTERFACE); |
| _tags.put(ReadOnly.class, READ_ONLY); |
| _tags.put(Type.class, TYPE); |
| } |
| |
| private final OpenJPAConfiguration _conf; |
| private final Log _log; |
| private MetaDataRepository _repos = null; |
| private ClassLoader _envLoader = null; |
| private boolean _override = false; |
| private int _mode = MODE_NONE; |
| |
| // packages and their parse modes |
| private final Map<Package, Integer> _pkgs = new HashMap<>(); |
| |
| // the class we were invoked to parse |
| protected Class<?> _cls = null; |
| protected Stack<Class<?>> _stack = new Stack<>(); |
| private File _file = null; |
| |
| /** |
| * Constructor; supply configuration. |
| */ |
| public AnnotationPersistenceMetaDataParser(OpenJPAConfiguration conf) { |
| _conf = conf; |
| _log = conf.getLog(OpenJPAConfiguration.LOG_METADATA); |
| } |
| |
| /** |
| * Configuration supplied on construction. |
| */ |
| public OpenJPAConfiguration getConfiguration() { |
| return _conf; |
| } |
| |
| /** |
| * Metadata log. |
| */ |
| public Log getLog() { |
| return _log; |
| } |
| |
| /** |
| * Returns the repository for this parser. If none has been set, |
| * create a new repository and sets it. |
| */ |
| public MetaDataRepository getRepository() { |
| if (_repos == null) { |
| MetaDataRepository repos = _conf.newMetaDataRepositoryInstance(); |
| MetaDataFactory mdf = repos.getMetaDataFactory(); |
| if (mdf instanceof DelegatingMetaDataFactory) |
| mdf = ((DelegatingMetaDataFactory) mdf).getInnermostDelegate(); |
| if (mdf instanceof PersistenceMetaDataFactory) |
| ((PersistenceMetaDataFactory) mdf).setAnnotationParser(this); |
| _repos = repos; |
| } |
| return _repos; |
| } |
| |
| /** |
| * Set the metadata repository for this parser. |
| */ |
| public void setRepository(MetaDataRepository repos) { |
| _repos = repos; |
| } |
| |
| /** |
| * Return the environmental class loader to pass on to parsed |
| * metadata instances. |
| */ |
| public ClassLoader getEnvClassLoader() { |
| return _envLoader; |
| } |
| |
| /** |
| * Set the environmental class loader to pass on to parsed |
| * metadata instances. |
| */ |
| public void setEnvClassLoader(ClassLoader loader) { |
| _envLoader = loader; |
| } |
| |
| /** |
| * Whether to allow later parses of mapping information to override |
| * earlier information for the same class. Defaults to false. Useful |
| * when a tool is mapping a class, so that annotation partial mapping |
| * information can be used even when mappings are stored in another |
| * location. |
| */ |
| public boolean getMappingOverride() { |
| return _override; |
| } |
| |
| /** |
| * Whether to allow later parses of mapping information to override |
| * earlier information for the same class. Defaults to false. Useful |
| * when a tool is mapping a class, so that annotation partial mapping |
| * information can be used even when mappings are stored in another |
| * location. |
| */ |
| public void setMappingOverride(boolean override) { |
| _override = override; |
| } |
| |
| /** |
| * The parse mode. |
| */ |
| public int getMode() { |
| return _mode; |
| } |
| |
| /** |
| * The parse mode. |
| */ |
| public void setMode(int mode, boolean on) { |
| if (mode == MODE_NONE) |
| _mode = MODE_NONE; |
| else if (on) |
| _mode |= mode; |
| else |
| _mode &= ~mode; |
| } |
| |
| /** |
| * The parse mode. |
| */ |
| public void setMode(int mode) { |
| _mode = mode; |
| } |
| |
| /** |
| * Convenience method for interpreting {@link #getMode}. |
| */ |
| protected boolean isMetaDataMode() { |
| return (_mode & MODE_META) != 0; |
| } |
| |
| /** |
| * Convenience method for interpreting {@link #getMode}. |
| */ |
| protected boolean isQueryMode() { |
| return (_mode & MODE_QUERY) != 0; |
| } |
| |
| /** |
| * Convenience method for interpreting {@link #getMode}. |
| */ |
| protected boolean isMappingMode() { |
| return (_mode & MODE_MAPPING) != 0; |
| } |
| |
| /** |
| * Returns true if we're in mapping mode or in metadata mode with |
| * mapping overide enabled. |
| */ |
| protected boolean isMappingOverrideMode() { |
| return isMappingMode() || (_override && isMetaDataMode()); |
| } |
| |
| /** |
| * Clear caches. |
| */ |
| public void clear() { |
| _stack.clear(); |
| _cls = null; |
| _file = null; |
| _pkgs.clear(); |
| } |
| |
| /** |
| * Parse persistence metadata for the given class. |
| */ |
| public void parse(Class<?> cls) { |
| if (_log.isTraceEnabled()) |
| _log.trace(_loc.get("parse-class", cls.getName())); |
| |
| _cls = cls; |
| _stack.push(cls); |
| try { |
| parsePackageAnnotations(); |
| ClassMetaData meta = parseClassAnnotations(); |
| updateSourceMode(meta); |
| } finally { |
| _stack.pop(); |
| _cls = _stack.isEmpty() ? null : _stack.peek(); |
| _file = null; |
| } |
| } |
| |
| /** |
| * Update the source mode to the class package and class to indicate that |
| * we've fully parsed them. |
| */ |
| private void updateSourceMode(ClassMetaData meta) { |
| if (_cls.getPackage() != null) |
| addSourceMode(_cls.getPackage(), _mode); |
| if (meta != null) |
| meta.setSourceMode(_mode, true); |
| } |
| |
| /** |
| * Parse information in package-level class annotations. |
| */ |
| private void parsePackageAnnotations() { |
| Package pkg = _cls.getPackage(); |
| if (pkg == null) |
| return; |
| |
| int pkgMode = getSourceMode(pkg); |
| if (pkgMode == 0 && _log.isTraceEnabled()) |
| _log.trace(_loc.get("parse-package", _cls.getName())); |
| if ((pkgMode & _mode) == _mode) // already visited |
| return; |
| |
| MetaDataTag tag; |
| for (Annotation anno : pkg.getDeclaredAnnotations()) { |
| tag = _tags.get(anno.annotationType()); |
| if (tag == null) { |
| handleUnknownPackageAnnotation(pkg, anno); |
| continue; |
| } |
| |
| switch (tag) { |
| case NATIVE_QUERIES: |
| if (isQueryMode() && (pkgMode & MODE_QUERY) == 0) |
| parseNamedNativeQueries(pkg, |
| ((NamedNativeQueries) anno).value()); |
| break; |
| case NATIVE_QUERY: |
| if (isQueryMode() && (pkgMode & MODE_QUERY) == 0) |
| parseNamedNativeQueries(pkg, (NamedNativeQuery) anno); |
| break; |
| case QUERIES: |
| if (isQueryMode() && (pkgMode & MODE_QUERY) == 0) |
| parseNamedQueries(pkg, ((NamedQueries) anno).value()); |
| break; |
| case QUERY: |
| if (isQueryMode() && (pkgMode & MODE_QUERY) == 0) |
| parseNamedQueries(pkg, (NamedQuery) anno); |
| break; |
| case STOREDPROCEDURE_QUERIES: |
| if (isQueryMode()) |
| parseNamedStoredProcedureQueries(pkg, ((NamedStoredProcedureQueries) anno).value()); |
| break; |
| case STOREDPROCEDURE_QUERY: |
| if (isQueryMode()) |
| parseNamedStoredProcedureQueries(pkg, ((NamedStoredProcedureQuery) anno)); |
| break; |
| case SEQ_GENERATOR: |
| if (isMappingOverrideMode() && |
| (pkgMode & MODE_MAPPING) == 0) |
| parseSequenceGenerator(pkg, (SequenceGenerator) anno); |
| break; |
| default: |
| throw new UnsupportedException(_loc.get("unsupported", pkg, |
| anno.toString())); |
| } |
| } |
| |
| // always parse mapping stuff after metadata stuff, in case there are |
| // dependencies on metadata |
| if (isMappingOverrideMode() && (pkgMode & MODE_MAPPING) == 0) |
| parsePackageMappingAnnotations(pkg); |
| } |
| |
| /** |
| * Parse package mapping annotations. |
| */ |
| protected void parsePackageMappingAnnotations(Package pkg) { |
| } |
| |
| /** |
| * Allow subclasses to handle unknown annotations. |
| */ |
| protected boolean handleUnknownPackageAnnotation(Package pkg, |
| Annotation anno) { |
| return false; |
| } |
| |
| /** |
| * The source mode for the given package. |
| */ |
| private int getSourceMode(Package pkg) { |
| Number num = _pkgs.get(pkg); |
| return (num == null) ? 0 : num.intValue(); |
| } |
| |
| /** |
| * Add to the source mode for the given package. |
| */ |
| private void addSourceMode(Package pkg, int mode) { |
| Integer num = _pkgs.get(pkg); |
| if (num == null) |
| num = mode; |
| else |
| num = num | mode; |
| _pkgs.put(pkg, num); |
| } |
| |
| /** |
| * Read annotations for the current type. |
| */ |
| private ClassMetaData parseClassAnnotations() { |
| // Check to see if there is cached metadata for the class that we are currently parsing. It |
| // is possible that one of the annotations (Entity, Embeddable, MappedSuperclass) is in the |
| // orm.xml. We still need to look at these files for other annotations and more importantly |
| // setup defaults (ie: Basic fields). |
| ClassMetaData m = getRepository().getCachedMetaData(_cls); |
| if (m == null) { |
| if (!AccessController.doPrivileged(J2DoPrivHelper.isAnnotationPresentAction(_cls, Entity.class)) |
| && !AccessController.doPrivileged(J2DoPrivHelper.isAnnotationPresentAction(_cls, Embeddable.class)) |
| && !AccessController.doPrivileged(J2DoPrivHelper.isAnnotationPresentAction(_cls, |
| MappedSuperclass.class))) |
| return null; |
| } |
| // find / create metadata |
| ClassMetaData meta = (m == null) ? getMetaData() : m; |
| if (meta == null) |
| return null; |
| |
| Entity entity = _cls.getAnnotation(Entity.class); |
| MappedSuperclass mapped = _cls.getAnnotation(MappedSuperclass.class); |
| Embeddable embeddable = _cls.getAnnotation(Embeddable.class); |
| if (isMetaDataMode()) { |
| meta.setAbstract(mapped != null); |
| if (embeddable != null) meta.setEmbeddable(); |
| // while the spec only provides for embedded exclusive, it doesn't |
| // seem hard to support otherwise |
| if (entity == null) |
| meta.setEmbeddedOnly(true); |
| else { |
| meta.setEmbeddedOnly(false); |
| if (!StringUtil.isEmpty(entity.name())) |
| meta.setTypeAlias(entity.name()); |
| } |
| } |
| |
| // track fetch groups to parse them after fields, since they |
| // rely on field metadata |
| FetchGroup[] fgs = null; |
| DetachedState detached = null; |
| |
| // track listeners since we need to merge them with entity callbacks |
| Collection<LifecycleCallbacks>[] listeners = null; |
| MetaDataTag tag; |
| for (Annotation anno : _cls.getDeclaredAnnotations()) { |
| tag = _tags.get(anno.annotationType()); |
| if (tag == null) { |
| handleUnknownClassAnnotation(meta, anno); |
| continue; |
| } |
| |
| switch (tag) { |
| case ENTITY_LISTENERS: |
| if (isMetaDataMode()) |
| listeners = parseEntityListeners(meta, |
| (EntityListeners) anno); |
| break; |
| case EXCLUDE_DEFAULT_LISTENERS: |
| if (isMetaDataMode()) |
| meta.getLifecycleMetaData() |
| .setIgnoreSystemListeners(true); |
| break; |
| case EXCLUDE_SUPERCLASS_LISTENERS: |
| if (isMetaDataMode()) |
| meta.getLifecycleMetaData().setIgnoreSuperclassCallbacks |
| (LifecycleMetaData.IGNORE_HIGH); |
| break; |
| case FLUSH_MODE: |
| if (isMetaDataMode()) |
| warnFlushMode(meta); |
| break; |
| case ID_CLASS: |
| if (isMetaDataMode()) { |
| Class<?> idClass = ((IdClass)anno).value(); |
| if (!Serializable.class.isAssignableFrom(idClass)) { |
| _log.warn(_loc.get("id-class-not-serializable", idClass, _cls).toString()); |
| } |
| meta.setObjectIdType(((IdClass) anno).value(), true); |
| } |
| break; |
| case NATIVE_QUERIES: |
| if (isQueryMode() && (meta.getSourceMode() & MODE_QUERY)==0) |
| parseNamedNativeQueries(_cls, |
| ((NamedNativeQueries) anno).value()); |
| break; |
| case NATIVE_QUERY: |
| if (isQueryMode() && (meta.getSourceMode() & MODE_QUERY)==0) |
| parseNamedNativeQueries(_cls, (NamedNativeQuery) anno); |
| break; |
| case QUERIES: |
| if (isQueryMode() && (meta.getSourceMode() & MODE_QUERY)==0) |
| parseNamedQueries(_cls, ((NamedQueries) anno).value()); |
| break; |
| case QUERY: |
| if (isQueryMode() && (meta.getSourceMode() & MODE_QUERY)==0) |
| parseNamedQueries(_cls, (NamedQuery) anno); |
| break; |
| case STOREDPROCEDURE_QUERIES: |
| if (isQueryMode()) |
| parseNamedStoredProcedureQueries(_cls, ((NamedStoredProcedureQueries) anno).value()); |
| break; |
| case STOREDPROCEDURE_QUERY: |
| if (isQueryMode()) |
| parseNamedStoredProcedureQueries(_cls, ((NamedStoredProcedureQuery) anno)); |
| break; |
| case SEQ_GENERATOR: |
| if (isMappingOverrideMode()) |
| parseSequenceGenerator(_cls, (SequenceGenerator) anno); |
| break; |
| case DATA_CACHE: |
| if (isMetaDataMode()) |
| parseDataCache(meta, (DataCache) anno); |
| break; |
| case DATASTORE_ID: |
| if (isMetaDataMode()) |
| parseDataStoreId(meta, (DataStoreId) anno); |
| break; |
| case DETACHED_STATE: |
| detached = (DetachedState) anno; |
| break; |
| case FETCH_GROUP: |
| if (isMetaDataMode()) |
| fgs = new FetchGroup[]{ (FetchGroup) anno }; |
| break; |
| case FETCH_GROUPS: |
| if (isMetaDataMode()) |
| fgs = ((FetchGroups) anno).value(); |
| break; |
| case MANAGED_INTERFACE: |
| if (isMetaDataMode()) |
| parseManagedInterface(meta, (ManagedInterface) anno); |
| break; |
| case ACCESS: |
| if (isMetaDataMode()) |
| parseAccess(meta, (Access)anno); |
| break; |
| case CACHEABLE: |
| if (isMetaDataMode()) { |
| parseCache(meta, (Cacheable) anno); |
| } |
| break; |
| default: |
| throw new UnsupportedException(_loc.get("unsupported", _cls, |
| anno.toString())); |
| } |
| } |
| |
| if (isMetaDataMode()) { |
| parseDetachedState(meta, detached); |
| |
| // merge callback methods with declared listeners |
| int[] highs = null; |
| if (listeners != null) { |
| highs = new int[listeners.length]; |
| for (int i = 0; i < listeners.length; i++) |
| if (listeners[i] != null) |
| highs[i] = listeners[i].size(); |
| } |
| recordCallbacks(meta, parseCallbackMethods(_cls, listeners, false, |
| false, getRepository()), highs, false); |
| |
| // scan possibly non-PC hierarchy for callbacks. |
| // redundant for PC superclass but we don't know that yet |
| // so let LifecycleMetaData determine that |
| if (_cls.getSuperclass() != null && |
| !Object.class.equals(_cls.getSuperclass())) { |
| recordCallbacks(meta, parseCallbackMethods(_cls.getSuperclass(), |
| null, true, false, getRepository()), null, true); |
| } |
| } |
| |
| for (FieldMetaData fmd : meta.getDeclaredFields()) |
| if (fmd.getManagement() == FieldMetaData.MANAGE_PERSISTENT) |
| parseMemberAnnotations(fmd); |
| // parse fetch groups after fields |
| if (fgs != null) |
| parseFetchGroups(meta, fgs); |
| |
| // always parse mapping after metadata in case there are dependencies |
| if (isMappingOverrideMode()) { |
| parseClassMappingAnnotations(meta); |
| for (FieldMetaData fmd : meta.getDeclaredFields()) |
| if (fmd.getManagement() == FieldMetaData.MANAGE_PERSISTENT) |
| parseMemberMappingAnnotations(fmd); |
| } |
| return meta; |
| } |
| |
| /** |
| * Set the explicit access type, if specified. |
| */ |
| private void parseAccess(ClassMetaData meta, Access access) { |
| if (access != null) { |
| meta.setAccessType(AccessCode.EXPLICIT |
| | (access.value() == AccessType.FIELD ? |
| AccessCode.FIELD : AccessCode.PROPERTY)); |
| } |
| } |
| |
| /** |
| * Parse class mapping annotations. |
| */ |
| protected void parseClassMappingAnnotations(ClassMetaData meta) { |
| } |
| |
| /** |
| * Allow subclasses to handle unknown annotations. |
| */ |
| protected boolean handleUnknownClassAnnotation(ClassMetaData meta, |
| Annotation anno) { |
| return false; |
| } |
| |
| /** |
| * Find or create metadata for the given type. May return null if |
| * this class has already been parsed fully. |
| */ |
| private ClassMetaData getMetaData() { |
| ClassMetaData meta = getRepository().getCachedMetaData(_cls); |
| if (meta != null |
| && ((isMetaDataMode() |
| && (meta.getSourceMode() & MODE_META) != 0) |
| || (isMappingMode() && (meta.getSourceMode() & MODE_MAPPING) != 0) ) ) { |
| if (_log.isWarnEnabled()) { |
| _log.warn(_loc.get("dup-metadata", _cls.getName())); |
| } |
| if(_log.isTraceEnabled()) { |
| _log.trace(String.format( |
| "MetaData originally obtained from file: %s under mode :%d with scope %s, and type :%d", |
| meta.getSourceName(), meta.getSourceMode(), meta.getSourceScope(), meta.getSourceType())); |
| } |
| return null; |
| } |
| |
| if (meta == null) { |
| meta = getRepository().addMetaData(_cls, getAccessCode(_cls)); |
| meta.setEnvClassLoader(_envLoader); |
| meta.setSourceMode(MODE_NONE); |
| meta.setSource(getSourceFile(), SourceTracker.SRC_ANNOTATIONS, getSourceFile() == null ? "" |
| : getSourceFile().getPath()); |
| } |
| return meta; |
| } |
| |
| /** |
| * Gets the explicit access for the class, if any. |
| * Explicit access type specification does not affect the access type of |
| * other entity classes or mapped super classes in the entity hierarchy. |
| */ |
| private int getAccessCode(Class<?> cls) { |
| int accessCode = AccessCode.UNKNOWN; |
| Access access = AccessController.doPrivileged( |
| J2DoPrivHelper.getAnnotationAction(cls, Access.class)); |
| if (access != null) { |
| accessCode |= AccessCode.EXPLICIT | |
| (access.value() == AccessType.FIELD ? |
| AccessCode.FIELD : AccessCode.PROPERTY); |
| } |
| return accessCode; |
| } |
| |
| /** |
| * Determine the source file we're parsing. |
| */ |
| protected File getSourceFile() { |
| if (_file != null) |
| return _file; |
| |
| Class<?> cls = _cls; |
| while (cls.getEnclosingClass() != null) |
| cls = cls.getEnclosingClass(); |
| |
| String rsrc = StringUtil.replace(cls.getName(), ".", "/"); |
| ClassLoader loader = AccessController.doPrivileged( |
| J2DoPrivHelper.getClassLoaderAction(cls)); |
| if (loader == null) |
| loader = AccessController.doPrivileged( |
| J2DoPrivHelper.getSystemClassLoaderAction()); |
| if (loader == null) |
| return null; |
| URL url = AccessController.doPrivileged( |
| J2DoPrivHelper.getResourceAction(loader, rsrc + ".java")); |
| if (url == null) { |
| url = AccessController.doPrivileged( |
| J2DoPrivHelper.getResourceAction(loader, rsrc + ".class")); |
| if (url == null) |
| return null; |
| } |
| try { |
| _file = new File(url.toURI()); |
| } catch (URISyntaxException e) { |
| } catch (IllegalArgumentException iae) { |
| // this is thrown when the URI is non-hierarchical (aka JBoss) |
| } |
| return _file; |
| } |
| |
| /** |
| * Parse @DataStoreId. |
| */ |
| private void parseDataStoreId(ClassMetaData meta, DataStoreId id) { |
| parseDataStoreId(meta, id.strategy(), id.generator()); |
| } |
| |
| static void parseDataStoreId(ClassMetaData meta, GenerationType strategy, |
| String generator) { |
| meta.setIdentityType(ClassMetaData.ID_DATASTORE); |
| |
| int strat = getGeneratedValueStrategy(meta, strategy, |
| generator); |
| if (strat != -1) |
| meta.setIdentityStrategy(strat); |
| else { |
| switch (strategy) { |
| case TABLE: |
| case SEQUENCE: |
| // technically we should have separate system table and |
| // sequence generators, but it's easier to just rely on |
| // the system org.apache.openjpa.Sequence setting for both |
| if (StringUtil.isEmpty(generator)) |
| meta.setIdentitySequenceName( |
| SequenceMetaData.NAME_SYSTEM); |
| else |
| meta.setIdentitySequenceName(generator); |
| break; |
| case AUTO: |
| meta.setIdentityStrategy(ValueStrategies.NATIVE); |
| break; |
| case IDENTITY: |
| meta.setIdentityStrategy(ValueStrategies.AUTOASSIGN); |
| break; |
| default: |
| throw new UnsupportedException(strategy.toString()); |
| } |
| } |
| } |
| |
| /** |
| * Warn that @FlushMode is not supported. |
| */ |
| private void warnFlushMode(Object context) { |
| if (_log.isWarnEnabled()) |
| _log.warn(_loc.get("unsupported", "FlushMode", context)); |
| } |
| |
| /** |
| * Parse @DataCache. |
| * |
| */ |
| private void parseDataCache(ClassMetaData meta, DataCache cache) { |
| parseDataCache(meta, cache.enabled(), cache.name(), cache.timeout()); |
| } |
| |
| static void parseDataCache(ClassMetaData meta, |
| boolean enabled, |
| String name, |
| int timeout) { |
| if (timeout != Integer.MIN_VALUE) { |
| meta.setDataCacheTimeout(timeout); |
| } |
| String cacheName = name; |
| if (StringUtil.isEmpty(cacheName)) { |
| cacheName = org.apache.openjpa.datacache.DataCache.NAME_DEFAULT; |
| } |
| meta.setDataCacheName(enabled ? cacheName : null); |
| } |
| |
| private void parseManagedInterface(ClassMetaData meta, |
| ManagedInterface iface) { |
| meta.setManagedInterface(true); |
| } |
| |
| /** |
| * Parse @DetachedState. The annotation may be null. |
| */ |
| private void parseDetachedState(ClassMetaData meta, |
| DetachedState detached) { |
| if (detached != null) { |
| if (!detached.enabled()) |
| meta.setDetachedState(null); |
| else if (StringUtil.isEmpty(detached.fieldName())) |
| meta.setDetachedState(ClassMetaData.SYNTHETIC); |
| else |
| meta.setDetachedState(detached.fieldName()); |
| } else { |
| Field[] fields = (Field[]) AccessController.doPrivileged( |
| J2DoPrivHelper.getDeclaredFieldsAction( |
| meta.getDescribedType())); |
| for (Field field : fields) |
| if (AccessController.doPrivileged(J2DoPrivHelper |
| .isAnnotationPresentAction(field, DetachedState.class))) |
| meta.setDetachedState(field.getName()); |
| } |
| } |
| |
| /** |
| * Parse @EntityListeners |
| */ |
| private Collection<LifecycleCallbacks>[] parseEntityListeners |
| (ClassMetaData meta, EntityListeners listeners) { |
| Class<?>[] classes = listeners.value(); |
| Collection<Class<?>> listenerColl = null; |
| Collection<LifecycleCallbacks>[] parsed = null; |
| for (Class<?> cls : classes) { |
| if (!_conf.getCallbackOptionsInstance().getAllowsDuplicateListener()) { |
| if (listenerColl == null) |
| listenerColl = new ArrayList<>(); |
| if (listenerColl.contains(cls)) |
| continue; |
| listenerColl.add(cls); |
| } |
| |
| parsed = parseCallbackMethods(cls, parsed, true, true, |
| getRepository()); |
| } |
| return parsed; |
| } |
| |
| /** |
| * Parse callback methods into the given array, and return that array, |
| * creating one if null. Each index into the array is a collection of |
| * callback adapters for that numeric event type. |
| * |
| * @param sups whether to scan superclasses |
| * @param listener whether this is a listener or not |
| */ |
| public static Collection<LifecycleCallbacks>[] parseCallbackMethods |
| (Class<?> cls, Collection<LifecycleCallbacks>[] callbacks, boolean sups, |
| boolean listener, MetaDataRepository repos) { |
| |
| if (cls == null) |
| throw new IllegalArgumentException("cls cannot be null"); |
| |
| // first sort / filter based on inheritance |
| Set<Method> methods = new TreeSet<>(MethodComparator. |
| getInstance()); |
| |
| int mods; |
| Class<?> sup = cls; |
| MethodKey key; |
| Set<MethodKey> seen = new HashSet<>(); |
| do { |
| for (Method m : (Method[]) AccessController.doPrivileged(J2DoPrivHelper.getDeclaredMethodsAction(sup))) { |
| mods = m.getModifiers(); |
| if (Modifier.isStatic(mods) || Modifier.isFinal(mods) || |
| Object.class.equals(m.getDeclaringClass())) |
| continue; |
| |
| key = new MethodKey(m); |
| if (!seen.contains(key)) { |
| methods.add(m); |
| seen.add(key); |
| } |
| } |
| sup = sup.getSuperclass(); |
| } while (sups && !Object.class.equals(sup)); |
| |
| OpenJPAConfiguration conf = repos.getConfiguration(); |
| for (Method m : methods) { |
| for (Annotation anno : (Annotation[]) AccessController |
| .doPrivileged(J2DoPrivHelper |
| .getDeclaredAnnotationsAction(m))) { |
| MetaDataTag tag = _tags.get(anno.annotationType()); |
| if (tag == null) |
| continue; |
| int[] events = MetaDataParsers.getEventTypes(tag, conf); |
| if (events == null) |
| continue; |
| |
| if (callbacks == null) |
| callbacks = (Collection<LifecycleCallbacks>[]) |
| new Collection[LifecycleEvent.ALL_EVENTS.length]; |
| |
| for (int e : events) { |
| if (callbacks[e] == null) |
| callbacks[e] = new ArrayList<>(3); |
| MetaDataParsers.validateMethodsForSameCallback(cls, |
| callbacks[e], m, tag, conf, repos.getLog()); |
| if (listener) { |
| callbacks[e].add(new BeanLifecycleCallbacks(cls, m, |
| false)); |
| } |
| else { |
| callbacks[e].add(new MethodLifecycleCallbacks(m, |
| false)); |
| } |
| } |
| } |
| } |
| return callbacks; |
| } |
| |
| /** |
| * Store lifecycle metadata. |
| */ |
| private void recordCallbacks(ClassMetaData cls, |
| Collection<LifecycleCallbacks>[] callbacks, int[] highs, |
| boolean superClass) { |
| if (callbacks == null) |
| return; |
| LifecycleMetaData meta = cls.getLifecycleMetaData(); |
| LifecycleCallbacks[] array; |
| for (int event : LifecycleEvent.ALL_EVENTS) { |
| if (callbacks[event] == null) |
| continue; |
| array = callbacks[event].toArray |
| (new LifecycleCallbacks[callbacks[event].size()]); |
| |
| if (superClass) { |
| meta.setNonPCSuperclassCallbacks(event, array, |
| (highs == null) ? 0 : highs[event]); |
| } else { |
| meta.setDeclaredCallbacks(event, array, |
| (highs == null) ? 0 : highs[event]); |
| } |
| } |
| } |
| |
| /** |
| * Create fetch groups. |
| * If FetchGroup A includes FetchGroup B, then a bi-link is set between |
| * A and B. Both A and B must be declared in the same Class. |
| * <br> |
| * Call {@link #parseFetchAttribute(ClassMetaData, org.apache.openjpa.meta.FetchGroup, FetchAttributeImpl)} |
| * only after the |
| * bi-links have been established, because a field f will not only add the |
| * fetch group A which explicitly includes f to its custom fetch groups but |
| * also will also add any fetch group B that includes A. |
| */ |
| static void parseFetchGroups(ClassMetaData meta, FetchGroup... groups) { |
| FetchGroupImpl[] fetchGroupImpls = new FetchGroupImpl[groups.length]; |
| for (int i = 0; i < groups.length; i++) { |
| FetchAttribute[] fetchAttributes = groups[i].attributes(); |
| FetchAttributeImpl[] fetchAttributeImpls= null; |
| if (fetchAttributes != null && fetchAttributes.length > 0) { |
| fetchAttributeImpls = new FetchAttributeImpl[fetchAttributes.length]; |
| for (int j = 0; j < fetchAttributes.length; j++) { |
| fetchAttributeImpls[j] = |
| new FetchAttributeImpl(fetchAttributes[j].name(), fetchAttributes[j].recursionDepth()); |
| } |
| } |
| fetchGroupImpls[i] = new FetchGroupImpl(groups[i].name(), groups[i].postLoad()); |
| if (fetchAttributeImpls != null) { |
| fetchGroupImpls[i].setAttributes(fetchAttributeImpls); |
| } |
| if (groups[i].fetchGroups() != null) { |
| fetchGroupImpls[i].setFetchGroups(groups[i].fetchGroups()); |
| } |
| } |
| |
| parseFetchGroups(meta, fetchGroupImpls); |
| } |
| |
| /** |
| * Parse fetch group input for the FetchGroup and FetchGroups annotations |
| * as well as for the fetch-group and fetch-groups XML metadata |
| * @param meta |
| * @param groups |
| */ |
| static void parseFetchGroups(ClassMetaData meta, FetchGroupImpl... groups) { |
| org.apache.openjpa.meta.FetchGroup fg; |
| for (FetchGroupImpl group : groups) { |
| if (StringUtil.isEmpty(group.name())) |
| throw new MetaDataException(_loc.get("unnamed-fg", meta)); |
| |
| fg = meta.addDeclaredFetchGroup(group.name()); |
| if (group.postLoad()) |
| fg.setPostLoad(true); |
| for (String s : group.fetchGroups()) { |
| fg.addDeclaredInclude(s); |
| } |
| } |
| // Add the parent-child style bi-links between fetch groups in a |
| // separate pass. |
| for (FetchGroupImpl group:groups) { |
| fg = meta.getFetchGroup(group.name()); |
| String[] includedFetchGropNames = fg.getDeclaredIncludes(); |
| for (String includedFectchGroupName:includedFetchGropNames) { |
| org.apache.openjpa.meta.FetchGroup child = |
| meta.getFetchGroup(includedFectchGroupName); |
| if (child == null) |
| throw new UserException(_loc.get("missing-included-fg", |
| meta.getDescribedType().getName(), fg.getName(), |
| includedFectchGroupName)); |
| child.addContainedBy(fg); |
| } |
| } |
| |
| for (FetchGroupImpl group : groups) { |
| fg = meta.getFetchGroup(group.name()); |
| for (FetchAttributeImpl attr : group.attributes()) |
| parseFetchAttribute(meta, fg, attr); |
| } |
| } |
| |
| |
| /** |
| * Set a field's fetch group. |
| */ |
| private static void parseFetchAttribute(ClassMetaData meta, |
| org.apache.openjpa.meta.FetchGroup fg, FetchAttributeImpl attr) { |
| FieldMetaData field = meta.getDeclaredField(attr.name()); |
| if (field == null |
| || field.getManagement() != FieldMetaData.MANAGE_PERSISTENT) |
| throw new MetaDataException(_loc.get("bad-fg-field", fg.getName(), |
| meta, attr.name())); |
| |
| field.setInFetchGroup(fg.getName(), true); |
| Set<String> parentFetchGroups = fg.getContainedBy(); |
| for (Object parentFetchGroup : parentFetchGroups) |
| field.setInFetchGroup(parentFetchGroup.toString(), true); |
| if (attr.recursionDepth() != Integer.MIN_VALUE) |
| fg.setRecursionDepth(field, attr.recursionDepth()); |
| } |
| |
| /** |
| * Read annotations for the given member. |
| */ |
| private void parseMemberAnnotations(FieldMetaData fmd) { |
| // look for persistence strategy in annotation table |
| Member member = getRepository().getMetaDataFactory().getDefaults(). |
| getBackingMember(fmd); |
| PersistenceStrategy pstrat = PersistenceMetaDataDefaults. |
| getPersistenceStrategy(fmd, member); |
| if (pstrat == null) |
| return; |
| fmd.setExplicit(true); |
| |
| AnnotatedElement el = (AnnotatedElement) member; |
| boolean lob = AccessController.doPrivileged(J2DoPrivHelper |
| .isAnnotationPresentAction(el, Lob.class)); |
| if (isMetaDataMode()) { |
| switch (pstrat) { |
| case BASIC: |
| parseBasic(fmd, (Basic) el.getAnnotation(Basic.class), lob); |
| break; |
| case MANY_ONE: |
| parseManyToOne(fmd, (ManyToOne) el.getAnnotation |
| (ManyToOne.class)); |
| break; |
| case ONE_ONE: |
| parseOneToOne(fmd, (OneToOne) el.getAnnotation |
| (OneToOne.class)); |
| break; |
| case EMBEDDED: |
| parseEmbedded(fmd, (Embedded) el.getAnnotation |
| (Embedded.class)); |
| break; |
| case ONE_MANY: |
| parseOneToMany(fmd, (OneToMany) el.getAnnotation |
| (OneToMany.class)); |
| break; |
| case MANY_MANY: |
| parseManyToMany(fmd, (ManyToMany) el.getAnnotation |
| (ManyToMany.class)); |
| break; |
| case PERS: |
| parsePersistent(fmd, (Persistent) el.getAnnotation |
| (Persistent.class)); |
| break; |
| case PERS_COLL: |
| parsePersistentCollection(fmd, (PersistentCollection) |
| el.getAnnotation(PersistentCollection.class)); |
| break; |
| case ELEM_COLL: |
| parseElementCollection(fmd, (ElementCollection) |
| el.getAnnotation(ElementCollection.class)); |
| break; |
| case PERS_MAP: |
| parsePersistentMap(fmd, (PersistentMap) |
| el.getAnnotation(PersistentMap.class)); |
| break; |
| case TRANSIENT: |
| break; |
| default: |
| throw new InternalException(); |
| } |
| } |
| |
| if (isMappingOverrideMode() && lob) |
| parseLobMapping(fmd); |
| |
| // extensions |
| MetaDataTag tag; |
| for (Annotation anno : el.getDeclaredAnnotations()) { |
| tag = _tags.get(anno.annotationType()); |
| if (tag == null) { |
| handleUnknownMemberAnnotation(fmd, anno); |
| continue; |
| } |
| |
| switch (tag) { |
| case ACCESS: |
| parseAccess(fmd, (Access)anno); |
| break; |
| case FLUSH_MODE: |
| if (isMetaDataMode()) |
| warnFlushMode(fmd); |
| break; |
| case GENERATED_VALUE: |
| if (isMappingOverrideMode()) |
| parseGeneratedValue(fmd, (GeneratedValue) anno); |
| break; |
| case ID: |
| case EMBEDDED_ID: |
| fmd.setPrimaryKey(true); |
| break; |
| case MAPPED_BY_ID: |
| parseMapsId(fmd, (MapsId)anno); |
| break; |
| case MAP_KEY: |
| if (isMappingOverrideMode()) |
| parseMapKey(fmd, (MapKey) anno); |
| break; |
| case MAP_KEY_CLASS: |
| if (isMappingOverrideMode()) |
| parseMapKeyClass(fmd, (MapKeyClass) anno); |
| break; |
| case ORDER_BY: |
| parseOrderBy(fmd, |
| (OrderBy) el.getAnnotation(OrderBy.class)); |
| break; |
| case SEQ_GENERATOR: |
| if (isMappingOverrideMode()) |
| parseSequenceGenerator(el, (SequenceGenerator) anno); |
| break; |
| case VERSION: |
| fmd.setVersion(true); |
| break; |
| case DEPENDENT: |
| if (isMetaDataMode() && ((Dependent) anno).value()) |
| fmd.setCascadeDelete(ValueMetaData.CASCADE_AUTO); |
| break; |
| case ELEM_DEPENDENT: |
| if (isMetaDataMode() && ((ElementDependent) anno).value()) |
| fmd.getElement().setCascadeDelete |
| (ValueMetaData.CASCADE_AUTO); |
| break; |
| case ELEM_TYPE: |
| if (isMetaDataMode()) |
| fmd.getElement().setTypeOverride(toOverrideType |
| (((ElementType) anno).value())); |
| break; |
| case EXTERNAL_VALS: |
| if (isMetaDataMode()) |
| fmd.setExternalValues(StringUtil.join(((ExternalValues) anno).value(), ",")); |
| break; |
| case EXTERNALIZER: |
| if (isMetaDataMode()) |
| fmd.setExternalizer(((Externalizer) anno).value()); |
| break; |
| case FACTORY: |
| if (isMetaDataMode()) |
| fmd.setFactory(((Factory) anno).value()); |
| break; |
| case INVERSE_LOGICAL: |
| if (isMetaDataMode()) |
| fmd.setInverse(((InverseLogical) anno).value()); |
| break; |
| case KEY_DEPENDENT: |
| if (isMetaDataMode() && ((KeyDependent) anno).value()) |
| fmd.getKey() |
| .setCascadeDelete(ValueMetaData.CASCADE_AUTO); |
| break; |
| case KEY_TYPE: |
| if (isMetaDataMode()) |
| fmd.getKey().setTypeOverride(toOverrideType(((KeyType) |
| anno).value())); |
| break; |
| case LOAD_FETCH_GROUP: |
| if (isMetaDataMode()) |
| fmd.setLoadFetchGroup(((LoadFetchGroup) anno).value()); |
| break; |
| case LRS: |
| if (isMetaDataMode()) |
| fmd.setLRS(((LRS) anno).value()); |
| break; |
| case READ_ONLY: |
| if (isMetaDataMode()) |
| parseReadOnly(fmd, (ReadOnly) anno); |
| break; |
| case TYPE: |
| if (isMetaDataMode()) |
| fmd.setTypeOverride(toOverrideType(((Type) anno). |
| value())); |
| break; |
| default: |
| throw new UnsupportedException(_loc.get("unsupported", fmd, |
| anno.toString())); |
| } |
| } |
| } |
| |
| /** |
| * Parse member mapping components. |
| */ |
| protected void parseMemberMappingAnnotations(FieldMetaData fmd) { |
| } |
| |
| /** |
| * Parse @Cache. |
| */ |
| private void parseCache(ClassMetaData meta, Cacheable cacheable) { |
| meta.setCacheEnabled(cacheable.value()); |
| } |
| |
| /** |
| * Allow subclasses to handle unknown annotations. |
| */ |
| protected boolean handleUnknownMemberAnnotation(FieldMetaData fmd, |
| Annotation anno) { |
| return false; |
| } |
| |
| /** |
| * Convert the given class to its OpenJPA type override equivalent. |
| */ |
| public static Class<?> toOverrideType(Class<?> cls) { |
| return (cls == Entity.class) |
| ? org.apache.openjpa.enhance.PersistenceCapable.class : cls; |
| } |
| |
| /** |
| * Parse @ReadOnly. |
| */ |
| private void parseReadOnly(FieldMetaData fmd, ReadOnly ro) { |
| if (ro.value() == UpdateAction.RESTRICT) |
| fmd.setUpdateStrategy(UpdateStrategies.RESTRICT); |
| else if (ro.value() == UpdateAction.IGNORE) |
| fmd.setUpdateStrategy(UpdateStrategies.IGNORE); |
| else |
| throw new InternalException(); |
| } |
| |
| /** |
| * Sets value generation information for the given field. |
| */ |
| private void parseGeneratedValue(FieldMetaData fmd, GeneratedValue gen) { |
| GenerationType strategy = gen.strategy(); |
| String generator = gen.generator(); |
| parseGeneratedValue(fmd, strategy, generator); |
| } |
| |
| /** |
| * Sets value generation information for the given field. |
| */ |
| static void parseGeneratedValue(FieldMetaData fmd, GenerationType strategy, |
| String generator) { |
| int strat = getGeneratedValueStrategy(fmd, strategy, generator); |
| if (strat != -1) |
| fmd.setValueStrategy(strat); |
| else { |
| switch (strategy) { |
| case TABLE: |
| case SEQUENCE: |
| // technically we should have separate system table and |
| // sequence generators, but it's easier to just rely on |
| // the system org.apache.openjpa.Sequence setting for both |
| if (StringUtil.isEmpty(generator)) |
| fmd.setValueSequenceName(SequenceMetaData.NAME_SYSTEM); |
| else |
| fmd.setValueSequenceName(generator); |
| break; |
| case AUTO: |
| fmd.setValueSequenceName(SequenceMetaData.NAME_SYSTEM); |
| break; |
| case IDENTITY: |
| fmd.setValueStrategy(ValueStrategies.AUTOASSIGN); |
| break; |
| default: |
| throw new UnsupportedException(strategy.toString()); |
| } |
| } |
| } |
| |
| /** |
| * Return the value strategy for the given generator, or -1 if the |
| * strategy depends on the <code>GenerationType</code> rather than the |
| * generator name. |
| */ |
| private static int getGeneratedValueStrategy(Object context, |
| GenerationType strategy, String generator) { |
| if (strategy != AUTO || StringUtil.isEmpty(generator)) |
| return -1; |
| |
| if (Generator.UUID_HEX.equals(generator)) |
| return ValueStrategies.UUID_HEX; |
| if (Generator.UUID_STRING.equals(generator)) |
| return ValueStrategies.UUID_STRING; |
| if (Generator.UUID_TYPE4_HEX.equals(generator)) |
| return ValueStrategies.UUID_TYPE4_HEX; |
| if (Generator.UUID_TYPE4_STRING.equals(generator)) |
| return ValueStrategies.UUID_TYPE4_STRING; |
| throw new MetaDataException(_loc.get("generator-bad-strategy", |
| context, generator)); |
| } |
| |
| /** |
| * Parse @Basic. Given annotation may be null. |
| */ |
| private void parseBasic(FieldMetaData fmd, Basic anno, boolean lob) { |
| Class<?> type = fmd.getDeclaredType(); |
| if (lob && type != String.class |
| && type != char[].class && type != Character[].class |
| && type != byte[].class && type != Byte[].class) |
| fmd.setSerialized(true); |
| else if (!lob) { |
| switch (fmd.getDeclaredTypeCode()) { |
| case JavaTypes.OBJECT: |
| if (Enum.class.isAssignableFrom(type)) |
| break; |
| // else no break |
| case JavaTypes.COLLECTION: |
| case JavaTypes.MAP: |
| case JavaTypes.PC: |
| case JavaTypes.PC_UNTYPED: |
| if (Serializable.class.isAssignableFrom(type)) |
| fmd.setSerialized(true); |
| else |
| throw new MetaDataException(_loc.get("bad-meta-anno", |
| fmd, "Basic")); |
| break; |
| case JavaTypes.ARRAY: |
| if (type == char[].class || type == Character[].class |
| || type == byte[].class || type == Byte[].class) |
| break; |
| if (Serializable.class.isAssignableFrom |
| (type.getComponentType())) |
| fmd.setSerialized(true); |
| else |
| throw new MetaDataException(_loc.get("bad-meta-anno", |
| fmd, "Basic")); |
| break; |
| } |
| } |
| |
| if (anno == null) |
| return; |
| fmd.setInDefaultFetchGroup(anno.fetch() == FetchType.EAGER); |
| if (!anno.optional()) |
| fmd.setNullValue(FieldMetaData.NULL_EXCEPTION); |
| } |
| |
| /** |
| * Parse @ManyToOne. |
| */ |
| private void parseManyToOne(FieldMetaData fmd, ManyToOne anno) { |
| if (!JavaTypes.maybePC(fmd.getValue())) |
| throw new MetaDataException(_loc.get("bad-meta-anno", fmd, |
| "ManyToOne")); |
| |
| // don't specifically exclude relation from DFG b/c that will prevent |
| // us from even reading the fk when reading from the primary table, |
| // which is not what most users will want |
| if (anno.fetch() == FetchType.EAGER) |
| fmd.setInDefaultFetchGroup(true); |
| if (!anno.optional()) |
| fmd.setNullValue(FieldMetaData.NULL_EXCEPTION); |
| if (anno.targetEntity() != void.class) |
| fmd.setTypeOverride(anno.targetEntity()); |
| setCascades(fmd, anno.cascade()); |
| fmd.setAssociationType(FieldMetaData.MANY_TO_ONE); |
| } |
| |
| /** |
| * Parse @OneToOne. |
| */ |
| private void parseOneToOne(FieldMetaData fmd, OneToOne anno) { |
| if (!JavaTypes.maybePC(fmd.getValue())) |
| throw new MetaDataException(_loc.get("bad-meta-anno", fmd, |
| "OneToOne")); |
| |
| // don't specifically exclude relation from DFG b/c that will prevent |
| // us from even reading the fk when reading from the primary table, |
| // which is not what most users will want |
| if (anno.fetch() == FetchType.EAGER) |
| fmd.setInDefaultFetchGroup(true); |
| if (!anno.optional()) |
| fmd.setNullValue(FieldMetaData.NULL_EXCEPTION); |
| |
| if (isMappingOverrideMode() && !StringUtil.isEmpty(anno.mappedBy())) |
| fmd.setMappedBy(anno.mappedBy()); |
| if (anno.targetEntity() != void.class) |
| fmd.setTypeOverride(anno.targetEntity()); |
| setCascades(fmd, anno.cascade()); |
| setOrphanRemoval(fmd, anno.orphanRemoval()); |
| fmd.setAssociationType(FieldMetaData.ONE_TO_ONE); |
| } |
| |
| /** |
| * Parse @Embedded. Given annotation may be null. |
| */ |
| private void parseEmbedded(FieldMetaData fmd, Embedded anno) { |
| if (!JavaTypes.maybePC(fmd.getValue())) |
| throw new MetaDataException(_loc.get("bad-meta-anno", fmd, |
| "Embedded")); |
| |
| fmd.setInDefaultFetchGroup(true); |
| fmd.setEmbedded(true); |
| |
| if (fmd.getEmbeddedMetaData() == null) |
| fmd.addEmbeddedMetaData(getAccessCode(fmd.getDeclaredType())); |
| } |
| |
| /** |
| * Parse @OneToMany. |
| */ |
| private void parseOneToMany(FieldMetaData fmd, OneToMany anno) { |
| switch (fmd.getDeclaredTypeCode()) { |
| case JavaTypes.ARRAY: |
| case JavaTypes.COLLECTION: |
| case JavaTypes.MAP: |
| if (JavaTypes.maybePC(fmd.getElement())) |
| break; |
| // no break |
| default: |
| throw new MetaDataException(_loc.get("bad-meta-anno", fmd, |
| "OneToMany")); |
| } |
| |
| fmd.setInDefaultFetchGroup(anno.fetch() == FetchType.EAGER); |
| if (isMappingOverrideMode() && !StringUtil.isEmpty(anno.mappedBy())) |
| fmd.setMappedBy(anno.mappedBy()); |
| if (anno.targetEntity() != void.class) |
| fmd.getElement().setDeclaredType(anno.targetEntity()); |
| setCascades(fmd.getElement(), anno.cascade()); |
| setOrphanRemoval(fmd.getElement(), anno.orphanRemoval()); |
| fmd.setAssociationType(FieldMetaData.ONE_TO_MANY); |
| } |
| |
| /** |
| * Parse @ManyToMany. |
| */ |
| private void parseManyToMany(FieldMetaData fmd, ManyToMany anno) { |
| switch (fmd.getDeclaredTypeCode()) { |
| case JavaTypes.ARRAY: |
| case JavaTypes.COLLECTION: |
| case JavaTypes.MAP: |
| if (fmd.getDeclaredType() == Properties.class || JavaTypes.maybePC(fmd.getElement())) |
| break; |
| // no break |
| default: |
| throw new MetaDataException(_loc.get("bad-meta-anno", fmd, |
| "ManyToMany")); |
| } |
| |
| fmd.setInDefaultFetchGroup(anno.fetch() == FetchType.EAGER); |
| if (isMappingOverrideMode() && !StringUtil.isEmpty(anno.mappedBy())) |
| fmd.setMappedBy(anno.mappedBy()); |
| if (anno.targetEntity() != void.class) |
| fmd.getElement().setDeclaredType(anno.targetEntity()); |
| setCascades(fmd.getElement(), anno.cascade()); |
| fmd.setAssociationType(FieldMetaData.MANY_TO_MANY); |
| } |
| |
| /** |
| * Parse @MapKey. |
| */ |
| private void parseMapKey(FieldMetaData fmd, MapKey anno) { |
| String name = anno.name(); |
| if (StringUtil.isEmpty(name)) |
| fmd.getKey().setValueMappedBy(ValueMetaData.MAPPED_BY_PK); |
| else |
| fmd.getKey().setValueMappedBy(name); |
| } |
| |
| /** |
| * Parse @MapKeyClass. |
| */ |
| private void parseMapKeyClass(FieldMetaData fmd, MapKeyClass anno) { |
| if (anno.value() != void.class) |
| fmd.getKey().setDeclaredType(anno.value()); |
| else |
| throw new IllegalArgumentException( |
| "The value of the MapClassClass cannot be null"); |
| } |
| |
| /** |
| * Parse @MapsId. |
| */ |
| private void parseMapsId(FieldMetaData fmd, MapsId anno) { |
| String value = anno.value(); |
| if (value != null) |
| fmd.setMappedByIdValue(value); |
| else |
| fmd.setMappedByIdValue(""); |
| } |
| |
| /** |
| * Setup the field as a LOB mapping. |
| */ |
| protected void parseLobMapping(FieldMetaData fmd) { |
| } |
| |
| /** |
| * Parse @OrderBy. |
| */ |
| private void parseOrderBy(FieldMetaData fmd, OrderBy anno) { |
| String dec = anno.value(); |
| if (fmd.isElementCollection() && |
| fmd.getElement().getEmbeddedMetaData() != null) { |
| if (dec.length() == 0 || dec.equals("ASC") || |
| dec.equals("DESC")) |
| throw new MetaDataException(_loc.get( |
| "invalid-orderBy", fmd)); |
| } |
| if (dec.length() == 0 || dec.equals("ASC")) |
| dec = Order.ELEMENT + " asc"; |
| else if (dec.equals("DESC")) |
| dec = Order.ELEMENT + " desc"; |
| |
| fmd.setOrderDeclaration(dec); |
| } |
| |
| /** |
| * Parse @Persistent. |
| */ |
| private void parsePersistent(FieldMetaData fmd, Persistent anno) { |
| switch (fmd.getDeclaredTypeCode()) { |
| case JavaTypes.ARRAY: |
| if (fmd.getDeclaredType() == byte[].class |
| || fmd.getDeclaredType() == Byte[].class |
| || fmd.getDeclaredType() == char[].class |
| || fmd.getDeclaredType() == Character[].class) |
| break; |
| // no break |
| case JavaTypes.COLLECTION: |
| case JavaTypes.MAP: |
| throw new MetaDataException(_loc.get("bad-meta-anno", fmd, |
| "Persistent")); |
| } |
| |
| if (!StringUtil.isEmpty(anno.mappedBy())) |
| fmd.setMappedBy(anno.mappedBy()); |
| fmd.setInDefaultFetchGroup(anno.fetch() == FetchType.EAGER); |
| if (!anno.optional()) |
| fmd.setNullValue(FieldMetaData.NULL_EXCEPTION); |
| setCascades(fmd, anno.cascade()); |
| if (anno.embedded()) { |
| if (!JavaTypes.maybePC(fmd.getValue())) |
| throw new MetaDataException(_loc.get("bad-meta-anno", fmd, |
| "Persistent(embedded=true)")); |
| fmd.setEmbedded(true); |
| if (fmd.getEmbeddedMetaData() == null) { |
| fmd.addEmbeddedMetaData(getAccessCode(fmd.getDeclaredType())); |
| } |
| } |
| } |
| |
| /** |
| * Parse @PersistentCollection. |
| */ |
| private void parsePersistentCollection(FieldMetaData fmd, |
| PersistentCollection anno) { |
| if (fmd.getDeclaredTypeCode() != JavaTypes.ARRAY |
| && fmd.getDeclaredTypeCode() != JavaTypes.COLLECTION) |
| throw new MetaDataException(_loc.get("bad-meta-anno", fmd, |
| "PersistentCollection")); |
| |
| fmd.setPersistentCollection(true); |
| |
| if (!StringUtil.isEmpty(anno.mappedBy())) |
| fmd.setMappedBy(anno.mappedBy()); |
| fmd.setInDefaultFetchGroup(anno.fetch() == FetchType.EAGER); |
| if (anno.elementType() != void.class) |
| fmd.getElement().setDeclaredType(anno.elementType()); |
| setCascades(fmd.getElement(), anno.elementCascade()); |
| if (anno.elementEmbedded()) { |
| if (!JavaTypes.maybePC(fmd.getElement())) |
| throw new MetaDataException(_loc.get("bad-meta-anno", fmd, |
| "PersistentCollection(embeddedElement=true)")); |
| fmd.getElement().setEmbedded(true); |
| if (fmd.getElement().getEmbeddedMetaData() == null) { |
| fmd.getElement().addEmbeddedMetaData( |
| getAccessCode(fmd.getElement().getDeclaredType())); |
| } |
| } |
| } |
| |
| /** |
| * Parse @ElementCollection. |
| */ |
| private void parseElementCollection(FieldMetaData fmd, |
| ElementCollection anno) { |
| // TODO: throw exception if the runtime env is OpenJpa 1.x |
| |
| if (fmd.getDeclaredTypeCode() != JavaTypes.COLLECTION && |
| fmd.getDeclaredTypeCode() != JavaTypes.MAP) |
| throw new MetaDataException(_loc.get("bad-meta-anno", fmd, |
| "ElementCollection")); |
| |
| if (anno.targetClass() != void.class) |
| fmd.getElement().setDeclaredType(anno.targetClass()); |
| fmd.setInDefaultFetchGroup(anno.fetch() == FetchType.EAGER); |
| fmd.setElementCollection(true); |
| ValueMetaData elem = fmd.getElement(); |
| boolean isEnum = elem.getDeclaredType().isEnum(); |
| if (!isEnum && JavaTypes.maybePC(elem)) { |
| elem.setEmbedded(true); |
| if (elem.getEmbeddedMetaData() == null) |
| elem.addEmbeddedMetaData(getAccessCode(elem.getDeclaredType())); |
| } |
| } |
| |
| /** |
| * Parse @PersistentMap. |
| */ |
| private void parsePersistentMap(FieldMetaData fmd, PersistentMap anno) { |
| if (fmd.getDeclaredTypeCode() != JavaTypes.MAP) |
| throw new MetaDataException(_loc.get("bad-meta-anno", fmd, |
| "PersistentMap")); |
| |
| fmd.setInDefaultFetchGroup(anno.fetch() == FetchType.EAGER); |
| if (anno.keyType() != void.class) |
| fmd.getKey().setDeclaredType(anno.keyType()); |
| if (anno.elementType() != void.class) |
| fmd.getElement().setDeclaredType(anno.elementType()); |
| setCascades(fmd.getKey(), anno.keyCascade()); |
| setCascades(fmd.getElement(), anno.elementCascade()); |
| if (anno.keyEmbedded()) { |
| if (!JavaTypes.maybePC(fmd.getKey())) |
| throw new MetaDataException(_loc.get("bad-meta-anno", fmd, |
| "PersistentMap(embeddedKey=true)")); |
| fmd.getKey().setEmbedded(true); |
| if (fmd.getKey().getEmbeddedMetaData() == null) { |
| fmd.getKey().addEmbeddedMetaData( |
| getAccessCode(fmd.getKey().getDeclaredType())); |
| } |
| } |
| if (anno.elementEmbedded()) { |
| if (!JavaTypes.maybePC(fmd.getElement())) |
| throw new MetaDataException(_loc.get("bad-meta-anno", fmd, |
| "PersistentMap(embeddedValue=true)")); |
| fmd.getElement().setEmbedded(true); |
| if (fmd.getElement().getEmbeddedMetaData() == null) |
| fmd.getElement().addEmbeddedMetaData( |
| getAccessCode(fmd.getElement().getDeclaredType())); |
| } |
| } |
| |
| /** |
| * Set cascades on relation. |
| */ |
| private void setCascades(ValueMetaData vmd, CascadeType[] cascades) { |
| for (CascadeType cascade : cascades) { |
| if (cascade == CascadeType.ALL || cascade == CascadeType.REMOVE) |
| vmd.setCascadeDelete(ValueMetaData.CASCADE_IMMEDIATE); |
| if (cascade == CascadeType.ALL || cascade == CascadeType.PERSIST) |
| vmd.setCascadePersist(ValueMetaData.CASCADE_IMMEDIATE, false); |
| if (cascade == CascadeType.ALL || cascade == CascadeType.MERGE) |
| vmd.setCascadeAttach(ValueMetaData.CASCADE_IMMEDIATE); |
| if (cascade == CascadeType.ALL || cascade == CascadeType.DETACH) |
| vmd.setCascadeDetach(ValueMetaData.CASCADE_IMMEDIATE); |
| if (cascade == CascadeType.ALL || cascade == CascadeType.REFRESH) |
| vmd.setCascadeRefresh(ValueMetaData.CASCADE_IMMEDIATE); |
| } |
| } |
| |
| private void setOrphanRemoval(ValueMetaData vmd, boolean orphanRemoval) { |
| if (orphanRemoval) |
| vmd.setCascadeDelete(ValueMetaData.CASCADE_AUTO); |
| } |
| |
| /** |
| * Parse @SequenceGenerator. |
| */ |
| private void parseSequenceGenerator(AnnotatedElement el, |
| SequenceGenerator gen) { |
| String name = gen.name(); |
| if (StringUtil.isEmpty(name)) |
| throw new MetaDataException(_loc.get("no-seq-name", el)); |
| |
| if (_log.isTraceEnabled()) |
| _log.trace(_loc.get("parse-sequence", name)); |
| |
| SequenceMetaData meta = getRepository().getCachedSequenceMetaData |
| (name); |
| if (meta != null) { |
| if (_log.isWarnEnabled()) |
| _log.warn(_loc.get("dup-sequence", name, el)); |
| return; |
| } |
| |
| // create new sequence |
| meta = getRepository().addSequenceMetaData(name); |
| String seq = gen.sequenceName(); |
| // Do not normalize the sequence name if it appears to be a plugin |
| if (seq.indexOf('(') == -1){ |
| seq = normalizeSequenceName(seq); |
| } |
| int initial = gen.initialValue(); |
| int allocate = gen.allocationSize(); |
| String schema = normalizeSchemaName(gen.schema()); |
| String catalog = normalizeCatalogName(gen.catalog()); |
| // don't allow initial of 0 b/c looks like def value |
| if (initial == 0) |
| initial = 1; |
| |
| // create plugin string from info |
| String clsName, props; |
| if (StringUtil.isEmpty(seq)) { |
| clsName = SequenceMetaData.IMPL_NATIVE; |
| props = null; |
| } else if (seq.indexOf('(') != -1) // plugin |
| { |
| clsName = Configurations.getClassName(seq); |
| props = Configurations.getProperties(seq); |
| seq = null; |
| } else { |
| clsName = SequenceMetaData.IMPL_NATIVE; |
| props = null; |
| } |
| |
| meta.setSequencePlugin(Configurations.getPlugin(clsName, props)); |
| meta.setSequence(seq); |
| meta.setInitialValue(initial); |
| meta.setAllocate(allocate); |
| meta.setSchema(schema); |
| meta.setCatalog(catalog); |
| meta.setSource(getSourceFile(), (el instanceof Class) ? el : null, |
| SourceTracker.SRC_ANNOTATIONS); |
| } |
| |
| /** |
| * Parse @NamedQuery. |
| */ |
| private void parseNamedQueries(AnnotatedElement el, NamedQuery... queries) { |
| QueryMetaData meta; |
| for (NamedQuery query : queries) { |
| if (StringUtil.isEmpty(query.name())) |
| throw new MetaDataException(_loc.get("no-query-name", el)); |
| if (StringUtil.isEmpty(query.query())) |
| throw new MetaDataException(_loc.get("no-query-string", |
| query.name(), el)); |
| |
| if (_log.isTraceEnabled()) |
| _log.trace(_loc.get("parse-query", query.name())); |
| |
| meta = getRepository().searchQueryMetaDataByName(query.name()); |
| if (meta != null) { |
| Class<?> definingType = meta.getDefiningType(); |
| if ((definingType == null || definingType != _cls) |
| && _log.isWarnEnabled()) { |
| _log.warn(_loc.get("dup-query", query.name(), el, |
| definingType)); |
| } |
| continue; |
| } |
| meta = getRepository().addQueryMetaData(_cls, query.name()); |
| meta.setLanguage(JPQLParser.LANG_JPQL); |
| meta.setQueryString(query.query()); |
| for (QueryHint hint : query.hints()) |
| meta.addHint(hint.name(), hint.value()); |
| LockModeType lmt = processNamedQueryLockModeType(query); |
| if (lmt != null && lmt != LockModeType.NONE) { |
| meta.addHint("openjpa.FetchPlan.ReadLockMode", lmt); |
| } |
| |
| meta.setSource(getSourceFile(), (el instanceof Class) ? el : null, |
| SourceTracker.SRC_ANNOTATIONS, getSourceFile() == null ? "" : getSourceFile().getPath()); |
| if (isMetaDataMode()) |
| meta.setSourceMode(MODE_META); |
| else if (isMappingMode()) |
| meta.setSourceMode(MODE_MAPPING); |
| else |
| meta.setSourceMode(MODE_QUERY); |
| } |
| } |
| |
| /** |
| * A private worker method that calculates the lock mode for an individual NamedQuery. If the NamedQuery is |
| * configured to use the NONE lock mode(explicit or implicit), this method will promote the lock to a READ |
| * level lock. This was done to allow for JPA1 apps to function properly under a 2.0 runtime. |
| */ |
| private LockModeType processNamedQueryLockModeType(NamedQuery query) { |
| LockModeType lmt = query.lockMode(); |
| if (query.lockMode() != null) { |
| String lm = _conf.getLockManager(); |
| boolean optimistic = _conf.getOptimistic(); |
| if (lm != null) { |
| lm = lm.toLowerCase(); |
| if (lm.contains("pessimistic")) { |
| if (lmt == LockModeType.NONE && !optimistic) { |
| if (_log.isWarnEnabled()) { |
| _log.warn(_loc.get("override-named-query-lock-mode", new String[] { "annotation", |
| query.name(), _cls.getName() })); |
| } |
| lmt = LockModeType.READ; |
| } |
| } |
| } |
| } |
| return lmt; |
| } |
| |
| /** |
| * Parse @NamedNativeQuery. |
| */ |
| private void parseNamedNativeQueries(AnnotatedElement el, |
| NamedNativeQuery... queries) { |
| QueryMetaData meta; |
| for (NamedNativeQuery query : queries) { |
| if (StringUtil.isEmpty(query.name())) |
| throw new MetaDataException(_loc.get("no-native-query-name", |
| el)); |
| if (StringUtil.isEmpty(query.query())) |
| throw new MetaDataException(_loc.get("no-native-query-string", |
| query.name(), el)); |
| |
| if (_log.isTraceEnabled()) |
| _log.trace(_loc.get("parse-native-query", query.name())); |
| |
| meta = getRepository().searchQueryMetaDataByName(query.name()); |
| if (meta != null) { |
| Class<?> defType = meta.getDefiningType(); |
| if ((defType != _cls) && _log.isWarnEnabled()) { |
| _log.warn(_loc.get("dup-query", query.name(), el, defType)); |
| } |
| continue; |
| } |
| |
| meta = getRepository().addQueryMetaData(null, query.name()); |
| meta.setLanguage(QueryLanguages.LANG_SQL); |
| meta.setQueryString(query.query()); |
| Class<?> res = query.resultClass(); |
| if (ImplHelper.isManagedType(getConfiguration(), res)) |
| meta.setCandidateType(res); |
| else if (!void.class.equals(res)) |
| meta.setResultType(res); |
| |
| if (!StringUtil.isEmpty(query.resultSetMapping())) |
| meta.setResultSetMappingName(query.resultSetMapping()); |
| for (QueryHint hint : query.hints()) |
| meta.addHint(hint.name(), hint.value()); |
| |
| meta.setSource(getSourceFile(), (el instanceof Class) ? el : null, |
| SourceTracker.SRC_ANNOTATIONS, getSourceFile() == null ? "" : getSourceFile().getPath()); |
| if (isMetaDataMode()) |
| meta.setSourceMode(MODE_META); |
| else if (isMappingMode()) |
| meta.setSourceMode(MODE_MAPPING); |
| else |
| meta.setSourceMode(MODE_QUERY); |
| } |
| } |
| |
| /** |
| * Set the explicit access type, if specified. |
| */ |
| private void parseAccess(FieldMetaData meta, Access access) { |
| if (access != null) { |
| meta.setAccessType(AccessCode.EXPLICIT |
| | (access.value() == AccessType.FIELD ? |
| AccessCode.FIELD : AccessCode.PROPERTY)); |
| } |
| } |
| |
| private static class MethodKey { |
| |
| private final Method _method; |
| |
| public MethodKey(Method m) { |
| _method = m; |
| } |
| |
| @Override |
| public int hashCode() { |
| int code = 46 * 12 + _method.getName().hashCode(); |
| for (Class<?> param : _method.getParameterTypes()) |
| code = 46 * code + param.hashCode(); |
| return code; |
| } |
| |
| @Override |
| public boolean equals(Object o) { |
| if (!(o instanceof MethodKey)) |
| return false; |
| Method other = ((MethodKey) o)._method; |
| if (!_method.getName().equals(other.getName())) |
| return false; |
| return Arrays.equals(_method.getParameterTypes(), |
| other.getParameterTypes()); |
| } |
| } |
| |
| private static class MethodComparator implements Comparator<Method> { |
| |
| private static MethodComparator INSTANCE = null; |
| |
| public static MethodComparator getInstance() { |
| if (INSTANCE == null) |
| INSTANCE = new MethodComparator(); |
| return INSTANCE; |
| } |
| |
| @Override |
| public int compare(Method m1, Method m2) { |
| Class<?> c1 = m1.getDeclaringClass(); |
| Class<?> c2 = m2.getDeclaringClass(); |
| if (!c1.equals(c2)) { |
| if (c1.isAssignableFrom(c2)) |
| return -1; |
| else |
| return 1; |
| } |
| int compare = m1.getName().compareTo(m2.getName ()); |
| if (compare != 0) { |
| return compare; |
| } |
| |
| Class<?>[] params1 = m1.getParameterTypes(); |
| Class<?>[] params2 = m2.getParameterTypes(); |
| compare = params1.length - params2.length; |
| if (compare != 0) { |
| return compare; |
| } |
| |
| // Just using the Method#hashCode() is not enough as it only contains |
| // the hash of the class + the hash of the NAME of the method... |
| // Thus if they have the same number of parameters, we need to compare them all |
| for (int i = 0; i < params1.length; i++) { |
| compare = params1[i].hashCode() - params2[i].hashCode(); |
| if (compare != 0) { |
| return compare; |
| } |
| } |
| |
| return 0; |
| } |
| } |
| |
| /** |
| * An internal class used to mimic the FetchGroup annotation. |
| * This is needed to process the fetch-group element in xml |
| * metadata with common code for the annotation. |
| */ |
| static class FetchGroupImpl { |
| private String name = ""; |
| private boolean postLoad = false; |
| private FetchAttributeImpl[] attributes = {}; |
| private String[] fetchGroups = {}; |
| |
| FetchGroupImpl(String name, boolean postLoad) |
| { |
| this.name = name; |
| this.postLoad = postLoad; |
| } |
| |
| public String name() { |
| return name; |
| } |
| |
| public boolean postLoad() { |
| return postLoad; |
| } |
| |
| public FetchAttributeImpl[] attributes() { |
| return attributes; |
| } |
| |
| public String[] fetchGroups() { |
| return fetchGroups; |
| } |
| |
| |
| public void setAttributes(FetchAttributeImpl[] attributes) { |
| this.attributes = attributes; |
| } |
| |
| public void setFetchGroups(String[] fetchGroups) { |
| this.fetchGroups = fetchGroups; |
| } |
| } |
| |
| /** |
| * An internal class used to mimic the FetchAttribute annotation. |
| * This is needed to process the fetch-attribute element in xml |
| * metadata with common code for the annotation. |
| */ |
| static class FetchAttributeImpl { |
| private String name = ""; |
| private int recursionDepth = Integer.MIN_VALUE; |
| |
| public FetchAttributeImpl(String name, int recursionDepth) { |
| this.name = name; |
| this.recursionDepth = recursionDepth; |
| } |
| |
| public String name() { |
| return name; |
| } |
| |
| public int recursionDepth() { |
| return recursionDepth; |
| } |
| |
| |
| } |
| |
| protected String normalizeSequenceName(String seqName) { |
| return seqName; |
| } |
| |
| protected String normalizeSchemaName(String schName) { |
| return schName; |
| } |
| |
| protected String normalizeCatalogName(String catName) { |
| return catName; |
| } |
| |
| |
| protected MultiQueryMetaData.Parameter.Mode toKernelParameterMode(ParameterMode mode) { |
| switch (mode) { |
| case IN : return MultiQueryMetaData.Parameter.Mode.IN; |
| case OUT: return MultiQueryMetaData.Parameter.Mode.OUT; |
| case INOUT: return MultiQueryMetaData.Parameter.Mode.INOUT; |
| case REF_CURSOR: return MultiQueryMetaData.Parameter.Mode.CURSOR; |
| default : return MultiQueryMetaData.Parameter.Mode.IN; |
| } |
| } |
| |
| protected void addSourceInfo(AnnotatedElement el, QueryMetaData meta) { |
| meta.setSource(getSourceFile(), (el instanceof Class) ? el : null, |
| SourceTracker.SRC_ANNOTATIONS, getSourceFile() == null ? "" : getSourceFile().getPath()); |
| if (isMetaDataMode()) |
| meta.setSourceMode(MODE_META); |
| else if (isMappingMode()) |
| meta.setSourceMode(MODE_MAPPING); |
| else |
| meta.setSourceMode(MODE_QUERY); |
| } |
| |
| protected void addHints(QueryMetaData meta, QueryHint...hints) { |
| for (QueryHint hint : hints) |
| meta.addHint(hint.name(), hint.value()); |
| |
| } |
| |
| private void parseNamedStoredProcedureQueries(AnnotatedElement el, NamedStoredProcedureQuery... procs) { |
| for (NamedStoredProcedureQuery proc : procs) { |
| if (StringUtil.isEmpty(proc.name())) |
| throw new MetaDataException(_loc.get("stored-proc-no-name", el)); |
| if (StringUtil.isEmpty(proc.procedureName())) |
| throw new MetaDataException(_loc.get("stored-proc-no-dbname", el)); |
| |
| // Query metadata name |
| MultiQueryMetaData meta = new MultiQueryMetaData(_cls, proc.name(), proc.procedureName(), false); |
| QueryMetaData existing = getRepository().addQueryMetaData(meta); |
| if (existing != null && existing.getDefiningType() != meta.getDefiningType()) { |
| getLog().warn(_loc.get("dup-query", meta.getName(), el, existing.getDefiningType())); |
| } |
| |
| // Important: The query string is the name of the database stored procedure |
| meta.setQueryString(proc.procedureName()); |
| |
| // For each mapping name/result class, add a component metadata |
| // The spec restricts that either ResultMappingName or ResultClasses be specified, but not both. |
| // This is relevant because the order of mapping must match the order in which the |
| // components are returned |
| Class<?>[] resultClasses = proc.resultClasses(); |
| String[] resultSetMappings = proc.resultSetMappings(); |
| if (resultClasses.length > 0 && resultSetMappings.length > 0) |
| throw new MetaDataException(_loc.get("stored-proc-both-mapping", el)); |
| for (Class<?> res : resultClasses) { |
| meta.addComponent(res); |
| } |
| for (String mapping : resultSetMappings) { |
| meta.addComponent(mapping); |
| } |
| StoredProcedureParameter[] params = proc.parameters(); |
| for (StoredProcedureParameter param : params) { |
| MultiQueryMetaData.Parameter p = new MultiQueryMetaData.Parameter( |
| param.name(), param.type(), toKernelParameterMode(param.mode())); |
| meta.registerParameter(p); |
| } |
| addHints(meta, proc.hints()); |
| addSourceInfo(el, meta); |
| } |
| } |
| } |
| |