Some Eclipse Foundation services are deprecated, or will be soon. Please ensure you've read this important communication.
Bug 324862 - IndexOutOfBoundsException in DatabaseSessionImpl.initializeDescriptors because @MapKey Annotation is not found.
Summary: IndexOutOfBoundsException in DatabaseSessionImpl.initializeDescriptors becaus...
Status: CLOSED FIXED
Alias: None
Product: z_Archived
Classification: Eclipse Foundation
Component: Eclipselink (show other bugs)
Version: unspecified   Edit
Hardware: PC Windows 7
: P2 major with 2 votes (vote)
Target Milestone: ---   Edit
Assignee: Tom Ware CLA
QA Contact:
URL:
Whiteboard: asm submitted_patch
Keywords:
: 304602 (view as bug list)
Depends on:
Blocks:
 
Reported: 2010-09-09 11:35 EDT by Hans Harz CLA
Modified: 2022-06-09 10:15 EDT (History)
5 users (show)

See Also:
tom.ware: iplog+


Attachments
Original jar and the one written by Proguard (and three dependencies) (1.11 MB, application/octet-stream)
2010-09-09 11:41 EDT, Hans Harz CLA
no flags Details
The workspace for this simple test (1.11 MB, application/octet-stream)
2010-09-09 11:45 EDT, Hans Harz CLA
no flags Details

Note You need to log in before you can comment on or make changes to this bug.
Description Hans Harz CLA 2010-09-09 11:35:02 EDT
Build Identifier: 2.1.1.v20100817-r8050

I wrote a verbose description of our problem here:
http://www.eclipse.org/forums/index.php?t=msg&th=175086&start=0&S=ba5ec765f86f8cbfd9bd40b431e06457

We obfuscate our jar with proguard (no classes that are used by eclipselink are obfuscated) and that leads to a problem reading in the annotations.

I created a very small sample with three classes (which just creates a h2 in-memory db with three entities). A zipped workspace is attached to this bug report. I also attached the raw jar and the obfuscated one in another zip file.

Decompiled with jd-gui the class TermEjb looks exactly the same in both versions. On byte code level there are a lot of differences, but it seems to be exactly the same java code.

This is the problematic part of the TermEjb code:

    @JoinTable(name = "TERMEJB_CUSTOMFIELDVALUEEJB", joinColumns = @JoinColumn(name = "TERMEJB_ID"), inverseJoinColumns = @JoinColumn(name = "CUSTOMFIELDS_ID"))
    @ManyToMany(cascade = {CascadeType.PERSIST, CascadeType.REMOVE})
    //this line works after obfuscation
    //private Map<Integer, CustomFieldValueEjb> customFields;
    
    //this next two lines do not work after obfuscation
    @MapKey(name = "field")
    private Map<CustomFieldEjb, CustomFieldValueEjb> customFields;



Reproducible: Always

Steps to Reproduce:
1. unzip the attached file TestJarObfuscatedAndOriginal.zip
2. type java -jar derFetteMainer.jar to run the original jar. You will see a short sys-out message:
DerFetteMainer ist am Start....
org.eclipse.persistence.internal.jpa.EntityManagerImpl@44b09697
DerFetteMainer hat funktioniert und ist fertig

3. Now try the same with the jar that was created by Proguard, by typing 
java -jar derFetteMainer_ob.jar. You will get a stack trace:

DerFetteMainer ist am Start....
[EL Severe]: 2010-09-09 17:13:14.784--ServerSession(1373164447)--Local Exception Stack:
Exception [EclipseLink-0] (Eclipse Persistence Services - 2.1.1.v20100817-r8050): org.eclipse.persistence.exceptions.Int
egrityException
Descriptor Exceptions:
---------------------------------------------------------

Exception [EclipseLink-93] (Eclipse Persistence Services - 2.1.1.v20100817-r8050): org.eclipse.persistence.exceptions.De
scriptorException
Exception Description: The table [CUSTOMFIELDVALUEEJB] is not present in this descriptor.
Descriptor: RelationalDescriptor(com.acrolinx.test.TermEjb --> [DatabaseTable(TERMEJB)])

Runtime Exceptions:
---------------------------------------------------------

java.lang.IndexOutOfBoundsException: Index: 0, Size: 0

        at org.eclipse.persistence.internal.sessions.DatabaseSessionImpl.initializeDescriptors(DatabaseSessionImpl.java:
471)
Comment 1 Hans Harz CLA 2010-09-09 11:41:50 EDT
Created attachment 178527 [details]
Original jar and the one written by Proguard (and three dependencies) 

1. unzip the file
2. add EclipseLink 2.1 and javax_persistence in the "derFetteMainer_lib" folder
3. just execute the both jars two see the difference.
- Original one "derFetteMainer.jar" executes fine, and prints three lines.
- The one written by Proguard "derFetteMainer_ob.jar" throws an EL-93 exception.
Comment 2 Hans Harz CLA 2010-09-09 11:45:21 EDT
Created attachment 178529 [details]
The workspace for this simple test

Complete workspace just add EclipseLink 2.1 and javax_persistence in the lib folder.
Comment 3 Hans Harz CLA 2010-09-10 10:53:00 EDT
I found a workaround  after stepping through asm and EclipseLink classes (it's horrible what's going on in the asm library. Why don't you use java reflection to get the annotations?!)

It is possible to patch the method
org.eclipse.persistence.internal.jpa.metadata.accessors.objects.MetadataAsmFactory.ClassMetadataVisitor.addAnnotations(Attribute, Map<String, MetadataAnnotation>)
like this:


        /**
         * If the attribute is an annotations attribute, add all annotations attached to it.
         */
        public void addAnnotations(final Attribute attr, final Map<String, MetadataAnnotation> annotations)
        {
            if (attr instanceof RuntimeVisibleAnnotations) {
                final RuntimeVisibleAnnotations visibleAnnotations = (RuntimeVisibleAnnotations) attr;
                for (final Iterator iterator = visibleAnnotations.annotations.iterator(); iterator.hasNext();) {
                    final Annotation visibleAnnotation = (Annotation) iterator.next();
                    // Only add annotations that we care about.
                    if ((visibleAnnotation.type.indexOf("javax/persistence") != -1)
                            || (visibleAnnotation.type.indexOf("org/eclipse/persistence") != -1)) {
                        final MetadataAnnotation annotation = buildAnnotation(visibleAnnotation);
                        annotations.put(annotation.getName(), annotation);
                    }
                }
            }

            //FIX: also read "sub-annotations" 
            if ((attr != null && attr.next instanceof RuntimeVisibleAnnotations)) {
                final RuntimeVisibleAnnotations visibleAnnotations = (RuntimeVisibleAnnotations) attr.next;
                for (final Iterator iterator = visibleAnnotations.annotations.iterator(); iterator.hasNext();) {
                    final Annotation visibleAnnotation = (Annotation) iterator.next();
                    // Only add annotations that we care about.
                    if ((visibleAnnotation.type.indexOf("javax/persistence") != -1)
                            || (visibleAnnotation.type.indexOf("org/eclipse/persistence") != -1)) {
                        final MetadataAnnotation annotation = buildAnnotation(visibleAnnotation);
                        if (!annotations.containsKey(annotation.getName())) {
                            annotations.put(annotation.getName(), annotation);
                        }
                    }
                }
            }
        }


But I think this is only a workaround and should be elaborated more precisely.
Comment 4 Tom Ware CLA 2010-09-13 10:53:26 EDT
FYI: ASM is used to get annotations to avoid some needing a special classloader to introspect the classes.
Comment 5 Tom Ware CLA 2010-09-20 08:50:57 EDT
Setting target and priority.  See the following page for details of the meanings of these fields:

http://wiki.eclipse.org/EclipseLink/Development/Bugs/Guidelines
Comment 6 Andrew Rustleund CLA 2010-10-07 11:39:26 EDT
Here is our version of a fix for this issue:

Note that this is a bit different (and more robust) than Hans's solution, as it checks ALL of the Attributes in the hierarchy, instead of just the first two. We ran into an issue where there were many Annotations that needed to be saved.

        /**
         * If the attribute is an annotations attribute, add all annotations attached to it.
         */
		public void addAnnotations(Attribute attr, Map<String, MetadataAnnotation> annotations) {
			Attribute toUse = attr;
			while (toUse != null) {
				if (toUse instanceof RuntimeVisibleAnnotations) {
					addAnnotationsHelper(annotations, (RuntimeVisibleAnnotations) toUse);
				}
				toUse = toUse.next;
			}
		}

		private void addAnnotationsHelper(Map<String, MetadataAnnotation> annotations, RuntimeVisibleAnnotations visibleAnnotations) {
			for (Iterator iterator = visibleAnnotations.annotations.iterator(); iterator.hasNext();) {
				Annotation visibleAnnotation = (Annotation) iterator.next();
				// Only add annotations that we care about.
				if ((visibleAnnotation.type.indexOf("javax/persistence") != -1) || (visibleAnnotation.type.indexOf("org/eclipse/persistence") != -1)) {
					MetadataAnnotation annotation = buildAnnotation(visibleAnnotation);
					annotations.put(annotation.getName(), annotation);
				}
			}
		}
Comment 7 Tom Ware CLA 2010-11-19 14:26:36 EST
Taking an initial look to determine feasability of including patch from this bug.
Comment 8 Tom Ware CLA 2010-11-22 11:31:24 EST
Checked in fix as suggested.

Tested with attached test case and JPA LRG.  I was unable to write a regression test due to the unavailabliity of an ofuscator in the test framework.

Reviewed: Tom Ware - reviewed user submitted fix
Comment 9 Guy Pelletier CLA 2010-12-29 15:55:01 EST
*** Bug 304602 has been marked as a duplicate of this bug. ***
Comment 10 Eclipse Webmaster CLA 2022-06-09 10:08:01 EDT
The Eclipselink project has moved to Github: https://github.com/eclipse-ee4j/eclipselink
Comment 11 Eclipse Webmaster CLA 2022-06-09 10:15:02 EDT
The Eclipselink project has moved to Github: https://github.com/eclipse-ee4j/eclipselink