File: PersistenceUnitProcessor.java

package info (click to toggle)
eclipselink 2.7.11-1
  • links: PTS, VCS
  • area: main
  • in suites: bookworm
  • size: 44,820 kB
  • sloc: java: 477,843; xml: 503; makefile: 21
file content (722 lines) | stat: -rw-r--r-- 32,668 bytes parent folder | download | duplicates (2)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
/*
 * Copyright (c) 1998, 2018 Oracle and/or its affiliates. All rights reserved.
 * Copyright (c) 1998, 2018 IBM Corporation. All rights reserved.
 *
 * This program and the accompanying materials are made available under the
 * terms of the Eclipse Public License v. 2.0 which is available at
 * http://www.eclipse.org/legal/epl-2.0,
 * or the Eclipse Distribution License v. 1.0 which is available at
 * http://www.eclipse.org/org/documents/edl-v10.php.
 *
 * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
 */

// Contributors:
//     Oracle - initial API and implementation from Oracle TopLink
//     10/09/2012-2.5 Guy Pelletier
//       - 374688: JPA 2.1 Converter support
//     07/08/2014-2.5 Jody Grassel (IBM Corporation)
//       - 439163: JSE Bootstrapping does not handle "wsjar" URLs referencing war-contained resources
//     08/29/2016 Jody Grassel
//       - 500441: Eclipselink core has System.getProperty() calls that are not potentially executed under doPriv()
//     11/23/2017: Scott Marlow
//       - 414974: allow eclipselink.archive.factory to be specified as an integration property of PersistenceProvider.createContainerEntityManagerFactory(PersistenceUnitInfo, Map)
package org.eclipse.persistence.internal.jpa.deployment;

import static org.eclipse.persistence.internal.jpa.metadata.MetadataConstants.JPA_CONVERTER;
import static org.eclipse.persistence.internal.jpa.metadata.MetadataConstants.JPA_EMBEDDABLE;
import static org.eclipse.persistence.internal.jpa.metadata.MetadataConstants.JPA_ENTITY;
import static org.eclipse.persistence.internal.jpa.metadata.MetadataConstants.JPA_MAPPED_SUPERCLASS;
import static org.eclipse.persistence.internal.jpa.metadata.MetadataConstants.JPA_STATIC_METAMODEL;

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.net.JarURLConnection;
import java.net.MalformedURLException;
import java.net.URISyntaxException;
import java.net.URL;
import java.net.URLDecoder;
import java.security.AccessController;
import java.security.PrivilegedActionException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Enumeration;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.StringTokenizer;

import javax.persistence.spi.PersistenceUnitInfo;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;

import org.eclipse.persistence.config.PersistenceUnitProperties;
import org.eclipse.persistence.config.SystemProperties;
import org.eclipse.persistence.exceptions.PersistenceUnitLoadingException;
import org.eclipse.persistence.exceptions.XMLParseException;
import org.eclipse.persistence.internal.helper.XMLHelper;
import org.eclipse.persistence.internal.jpa.deployment.xml.parser.PersistenceContentHandler;
import org.eclipse.persistence.internal.jpa.deployment.xml.parser.XMLException;
import org.eclipse.persistence.internal.jpa.deployment.xml.parser.XMLExceptionHandler;
import org.eclipse.persistence.internal.jpa.metadata.MetadataProcessor;
import org.eclipse.persistence.internal.jpa.metadata.MetadataProject;
import org.eclipse.persistence.internal.jpa.metadata.accessors.objects.MetadataAnnotation;
import org.eclipse.persistence.internal.jpa.metadata.accessors.objects.MetadataClass;
import org.eclipse.persistence.internal.security.PrivilegedAccessHelper;
import org.eclipse.persistence.internal.security.PrivilegedGetSystemProperty;
import org.eclipse.persistence.internal.security.PrivilegedNewInstanceFromClass;
import org.eclipse.persistence.jpa.Archive;
import org.eclipse.persistence.jpa.ArchiveFactory;
import org.eclipse.persistence.logging.AbstractSessionLog;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
import org.xml.sax.XMLReader;

/**
 * INTERNAL:
 * Utility Class that deals with persistence archives for EJB 3.0
 * Provides functions like searching for persistence archives, processing
 * persistence.xml and searching for Entities in a Persistence archive
 */
public class PersistenceUnitProcessor {
    /**
     * Passed to processORMetadata method to indicate processing mode.
     * ALL used when independent persistence unit is created.
     * COMPOSITE_MEMBER_INITIAL, COMPOSITE_MEMBER_MIDDLE and COMPOSITE_MEMBER_FINAL used for a composite member persistence unit.
     */
    public enum Mode {
        ALL,
        COMPOSITE_MEMBER_INITIAL,
        COMPOSITE_MEMBER_MIDDLE,
        COMPOSITE_MEMBER_FINAL
    }

    /**
     * Cache the ArchiveFactory used to derive Archives.  This allows applications
     * to set the ArchiveFactory
     */
    public static ArchiveFactory ARCHIVE_FACTORY = null;

    /** Path to application classes directory in WAR file. */
    private static final String WEBINF_CLASSES_STR = "WEB-INF/classes/";

    /** Length of application classes directory path String. */
    private static final int WEBINF_CLASSES_LEN = WEBINF_CLASSES_STR.length();

    /**
     * Entries in a zip file are directory entries using slashes to separate
     * them. Build a class name using '.' instead of slash and removing the
     * '.class' extension.
     */
    public static String buildClassNameFromEntryString(String classEntryString){
        String classNameForLoader = classEntryString;
        if (classEntryString.endsWith(".class")){
            classNameForLoader = classNameForLoader.substring(0, classNameForLoader.length() - 6);
            classNameForLoader = classNameForLoader.replace("/", ".");
        }
        return classNameForLoader;
    }

    /**
     * Build a set that contains all the class names at a URL.
     * @return a Set of class name strings
     */
    public static Set<String> buildClassSet(PersistenceUnitInfo persistenceUnitInfo, Map properties){
        Set<String> set = new HashSet<String>();
        set.addAll(persistenceUnitInfo.getManagedClassNames());
        ClassLoader loader = persistenceUnitInfo.getClassLoader();
        Iterator i = persistenceUnitInfo.getJarFileUrls().iterator();
        while (i.hasNext()) {
            set.addAll(getClassNamesFromURL((URL)i.next(), loader, properties));
        }
        if (!persistenceUnitInfo.excludeUnlistedClasses()){
            set.addAll(getClassNamesFromURL(persistenceUnitInfo.getPersistenceUnitRootUrl(), loader, properties));
        }
        // No longer need to add classes from XML, as temp class loader is only used for sessions.xml.
        return set;
    }

    /**
     * Create a list of the entities that will be deployed. This list is built
     * from the information provided in the PersistenceUnitInfo argument.
     * The list contains Classes specified in the PersistenceUnitInfo's class
     * list and also files that are annotated with @Entity and @Embeddable in
     * the jar files provided in the persistence info. This list of classes will
     * used to build a deployment project and to decide what classes
     * to weave.
     */
    public static Collection<MetadataClass> buildEntityList(MetadataProcessor processor, ClassLoader loader) {
        ArrayList<MetadataClass> entityList = new ArrayList<MetadataClass>();
        for (String className : processor.getProject().getWeavableClassNames()) {
            entityList.add(processor.getMetadataFactory().getMetadataClass(className));
        }
        return entityList;
    }

    /**
     * Determine the URL path to the persistence unit
     * @param pxmlURL - Encoded URL containing the pu
     * @return
     * @throws IOException
     */
    public static URL computePURootURL(URL pxmlURL, String descriptorLocation) throws IOException, URISyntaxException {
        StringTokenizer tokenizer = new StringTokenizer(descriptorLocation, "/\\");
        int descriptorDepth = tokenizer.countTokens() - 1;
        URL result;
        String protocol = pxmlURL.getProtocol();
        if("file".equals(protocol)) { // NOI18N
            StringBuffer path = new StringBuffer();
            boolean firstElement = true;
            for (int i=0;i<descriptorDepth;i++){
                if (!firstElement){
                    path.append("/"); // 315097 URL use standard separators
                }
                path.append("..");
                firstElement = false;
            }
            // e.g. file:/tmp/META-INF/persistence.xml
            // 210280: any file url will be assumed to always reference a file (not a directory)
            result = new URL(pxmlURL, path.toString()); // NOI18N
        } else if("jar".equals(protocol)) { // NOI18N
            // e.g. jar:file:/tmp/a_ear/b.jar!/META-INF/persistence.xml
            JarURLConnection conn =
                    JarURLConnection.class.cast(pxmlURL.openConnection());
            result = conn.getJarFileURL();
        } else if("zip".equals(protocol)) { // NOI18N
            // e.g. zip:/tmp/a_ear/b.jar!/META-INF/persistence.xml
            // stolen from java.net.JarURLConnection.parseSpecs method
            String spec = pxmlURL.getFile();
            int separator = spec.lastIndexOf("!/");
            if (separator == -1) {
                separator = spec.length() - 1;
            }
            result = new File(spec.substring(0, separator++)).toURL();
        } else if("wsjar".equals(protocol)) { // NOI18N
            // e.g. wsjar:file:/tmp/a_ear/b.jar!/META-INF/persistence.xml
            // but WS gives use jar:file:..., so we need to match it.
            String spec = pxmlURL.getFile();
            int separator = spec.lastIndexOf("!/");
            if (separator == -1) {
                separator = spec.length();
            } else {
                // If this doesn't reference a war file with a properly located persistence.xml,
                // then chop off everything after the "!/" marker and assume it is a normal jar.
                // Else, if the wsjar URL references a file with a ".war" extension, and its entry
                // starts with WEB-INF/classes/, then the calculated persistence unit root should
                // be wsjar:path/to/a.war!/WEB-INF/classes/ as per JPA 2.1 Spec section 8.2 "Persistence Unit Packaging".
                separator += 2;
                // Filter out invalid scenarios such as wsjar:file:/a/path/to/my.war!/foo/WEB-INF/classes/META-INF/persistence.xml
                if (spec.regionMatches(true, separator - 6, ".war", 0, 4) &&
                        spec.regionMatches(true, separator, WEBINF_CLASSES_STR, 0, WEBINF_CLASSES_LEN)) {
                    separator += WEBINF_CLASSES_LEN;
                }
            }
            result = new URL("jar", "", spec.substring(0, separator));
        } else if ("bundleentry".equals(protocol)) {
            // mkeith - add bundle protocol cases
            result = new URL("bundleentry://" + pxmlURL.getAuthority());
        } else if ("bundleresource".equals(protocol)) {
            result = new URL("bundleresource://" + pxmlURL.getAuthority());
        } else {
            StringBuffer path = new StringBuffer();
            for (int i=0;i<descriptorDepth;i++){
                path.append("../"); // 315097 URL use standard separators
            }
            // some other protocol
            result = new URL(pxmlURL, path.toString()); // NOI18N
        }
        result = fixUNC(result);
        return result;
    }


    /**
     * This method fixes incorrect authority attribute
     * that is set by JDK when UNC is used in classpath.
     * See JDK bug #6585937 and GlassFish issue #3209 for more details.
     */
    private static URL fixUNC(URL url) throws URISyntaxException, MalformedURLException, UnsupportedEncodingException
    {
        String protocol = url.getProtocol();
        if (!"file".equalsIgnoreCase(protocol)) {
            return url;
        }
        String authority= url.getAuthority();
        String file = url.getFile();
        if (authority != null) {
            AbstractSessionLog.getLog().finer(
                    "fixUNC: before fixing: url = " + url + ", authority = " + authority + ", file = " + file);
            assert(url.getPort() == -1);

            // See GlassFish issue https://glassfish.dev.java.net/issues/show_bug.cgi?id=3209 and
            // JDK issue http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6585937
            // When there is UNC path in classpath, the classloader.getResource
            // returns a file: URL with an authority component in it.
            // The URL looks like this:
            // file://ahost/afile.
            // Interestingly, authority and file components for the above URL
            // are either "ahost" and "/afile" or "" and "//ahost/afile" depending on
            // how the URL is obtained. If classpath is set as a jar with UNC,
            // the former is true, if the classpath is set as a directory with UNC,
            // the latter is true.
            String prefix = "";
            if (authority.length() > 0) {
                prefix = "////";
            } else if (file.startsWith("//")) {
                prefix = "//";
            }
            file = prefix.concat(authority).concat(file);
            url = new URL(protocol, null, file);
            AbstractSessionLog.getLog().finer(
                    "fixUNC: after fixing: url = " + url + ", authority = " + url.getAuthority() + ", file = " + url.getFile());
        }
        return url;
    }

    /**
     * Search the classpath for persistence archives. A persistence archive is
     * defined as any part of the class path that contains a META-INF directory
     * with a persistence.xml file in it. Return a list of the URLs of those
     * files. Use the current thread's context classloader to get the classpath.
     * We assume it is a URL class loader.
     */
    public static Set<Archive> findPersistenceArchives(){
        ClassLoader threadLoader = Thread.currentThread().getContextClassLoader();
        return findPersistenceArchives(threadLoader);
    }

    /**
     * Search the classpath for persistence archives. A persistence archive is
     * defined as any part of the class path that contains a META-INF directory
     * with a persistence.xml file in it. Return a list of {@link Archive}
     * representing the root of those files. It is the caller's responsibility
     * to close all the archives.
     *
     * @param loader the class loader to get the class path from
     */
    public static Set<Archive> findPersistenceArchives(ClassLoader loader){
        // allow alternate persistence location to be specified via system property.  This will allow persistence units
        // with alternate persistence xml locations to be weaved
        String descriptorLocation = PrivilegedAccessHelper.getSystemProperty(PersistenceUnitProperties.ECLIPSELINK_PERSISTENCE_XML, PersistenceUnitProperties.ECLIPSELINK_PERSISTENCE_XML_DEFAULT);

        return findPersistenceArchives(loader, descriptorLocation);
    }

    /**
     * Return a list of Archives representing the root of the persistence descriptor.
     * It is the caller's responsibility to close all the archives.
     *
     * @param loader the class loader to get the class path from
     */
    public static Set<Archive> findPersistenceArchives(ClassLoader loader, String descriptorPath){
        Archive archive = null;

        Set<Archive> archives = new HashSet<Archive>();

        // See if we are talking about an embedded descriptor
        int splitPosition = descriptorPath.indexOf("!/");

        try {
            // If not embedded descriptor then just use the regular descriptor path
            if (splitPosition == -1) {
                Enumeration<URL> resources = loader.getResources(descriptorPath);
                while (resources.hasMoreElements()){

                    URL descUrl = resources.nextElement();
                    if (descUrl != null) {
                        URL puRootUrl = computePURootURL(descUrl, descriptorPath);
                        archive = PersistenceUnitProcessor.getArchiveFactory(loader).createArchive(puRootUrl, descriptorPath, null);

                       // archive = new BundleArchive(puRootUrl, descUrl);
                        if (archive != null){
                            archives.add(archive);
                        }
                    }
                }
            } else {
                // It is an embedded archive, so split up the parts
                String jarPrefixPath = descriptorPath.substring(0, splitPosition);
                String descPath = descriptorPath.substring(splitPosition+2);
                // TODO This causes the bundle to be resolved (not what we want)!
                URL prefixUrl = loader.getResource(jarPrefixPath);
                archive = PersistenceUnitProcessor.getArchiveFactory(loader).createArchive(prefixUrl, descPath, null);

                if (archive != null){
                    archives.add(archive);
                }
            }
        } catch (Exception ex){
            //clean up first
            for (Archive a : archives){
                a.close();
            }
            throw PersistenceUnitLoadingException.exceptionSearchingForPersistenceResources(loader, ex);
        }
        return archives;
    }

    /**
     * Return a list of Archives representing the root of the persistence descriptor.
     * It is the caller's responsibility to close all the archives.
     *
     * @param loader the class loader to get the class path from
     */
    public static Set<Archive> findPersistenceArchives(ClassLoader loader, String descriptorPath, List<URL> jarFileUrls, Map properties) {
        Archive archive = null;

        Set<Archive> archives = new HashSet<Archive>();

        // See if we are talking about an embedded descriptor
        // If not embedded descriptor then just use the regular descriptor path
        int splitPosition = descriptorPath.indexOf("!/");
        if (splitPosition != -1) {
            // It is an embedded archive, so split up the parts
            descriptorPath = descriptorPath.substring(splitPosition+2);
        }

        try {
            for(int i=0; i < jarFileUrls.size(); i++) {
                URL puRootUrl = jarFileUrls.get(i);
                archive = PersistenceUnitProcessor.getArchiveFactory(loader, properties).createArchive(puRootUrl, descriptorPath, null);

                // archive = new BundleArchive(puRootUrl, descUrl);
                if (archive != null){
                    archives.add(archive);
                }
            }
        } catch (Exception ex){
            //clean up first
            for (Archive a : archives){
                a.close();
            }
            throw PersistenceUnitLoadingException.exceptionSearchingForPersistenceResources(loader, ex);
        }
        return archives;
    }

    public static Set<SEPersistenceUnitInfo> getPersistenceUnits(ClassLoader loader, Map m, List<URL> jarFileUrls) {
        String descriptorPath = (String) m.get(PersistenceUnitProperties.ECLIPSELINK_PERSISTENCE_XML);
        if(descriptorPath == null) {
            descriptorPath = PrivilegedAccessHelper.getSystemProperty(PersistenceUnitProperties.ECLIPSELINK_PERSISTENCE_XML, PersistenceUnitProperties.ECLIPSELINK_PERSISTENCE_XML_DEFAULT);
        }
        Set<Archive> archives = findPersistenceArchives(loader, descriptorPath, jarFileUrls, m);
        Set<SEPersistenceUnitInfo> puInfos = new HashSet();
        try {
            for(Archive archive : archives) {
                List<SEPersistenceUnitInfo> puInfosFromArchive = getPersistenceUnits(archive, loader);
                puInfos.addAll(puInfosFromArchive);
            }
        } finally {
            for(Archive archive : archives) {
                archive.close();
            }
        }
        return puInfos;
    }

    public static ArchiveFactory getArchiveFactory(ClassLoader loader){
        return getArchiveFactory(loader, null);
    }

    public static ArchiveFactory getArchiveFactory(ClassLoader loader, Map properties){
        if (ARCHIVE_FACTORY != null){
            return ARCHIVE_FACTORY;
        }

        ArchiveFactory factory = null;
        String factoryClassName = PrivilegedAccessHelper.shouldUsePrivilegedAccess()
                ? AccessController.doPrivileged(new PrivilegedGetSystemProperty(SystemProperties.ARCHIVE_FACTORY))
                : System.getProperty(SystemProperties.ARCHIVE_FACTORY);

        if (factoryClassName == null && properties != null) {
            Object name = properties.get(SystemProperties.ARCHIVE_FACTORY);
            if(name instanceof String) {
                factoryClassName = (String) name;
            }
        }

        if (factoryClassName == null) {
            return new ArchiveFactoryImpl();
        } else {
            try {
                if (loader != null) {
                    Class archiveClass = loader.loadClass(factoryClassName);
                    if (PrivilegedAccessHelper.shouldUsePrivilegedAccess()){
                        try {
                            factory = (ArchiveFactory)AccessController.doPrivileged(new PrivilegedNewInstanceFromClass(archiveClass));
                        } catch (PrivilegedActionException exception) {
                            throw PersistenceUnitLoadingException.exceptionCreatingArchiveFactory(factoryClassName, exception);
                        }
                    } else {
                        factory = (ArchiveFactory)PrivilegedAccessHelper.newInstanceFromClass(archiveClass);
                    }
                }
            } catch (ClassNotFoundException cnfe) {
                throw PersistenceUnitLoadingException.exceptionCreatingArchiveFactory(factoryClassName, cnfe);
            } catch (IllegalAccessException iae) {
                throw PersistenceUnitLoadingException.exceptionCreatingArchiveFactory(factoryClassName, iae);
            } catch (InstantiationException ie) {
                throw PersistenceUnitLoadingException.exceptionCreatingArchiveFactory(factoryClassName, ie);
            }
        }

        return factory;
    }

    public static Set<String> getClassNamesFromURL(URL url, ClassLoader loader, Map properties) {
        Set<String> classNames = new HashSet<String>();
        Archive archive = null;
        try {
            archive = PersistenceUnitProcessor.getArchiveFactory(loader, properties).createArchive(url, properties);

            if (archive != null) {
                for (Iterator<String> entries = archive.getEntries(); entries.hasNext();) {
                    String entry = entries.next();
                    if (entry.endsWith(".class")){ // NOI18N
                        classNames.add(buildClassNameFromEntryString(entry));
                    }
                }
            }
        } catch (URISyntaxException e) {
            throw new RuntimeException("url = [" + url + "]", e);  // NOI18N
        } catch (IOException e) {
            throw new RuntimeException("url = [" + url + "]", e);  // NOI18N
        } finally {
            if (archive != null) {
                archive.close();
            }
        }
        return classNames;
    }

    /**
     * Return if a given class is annotated with @Embeddable.
     */
    public static MetadataAnnotation getConverterAnnotation(MetadataClass candidateClass){
        return candidateClass.getAnnotation(JPA_CONVERTER);
    }

    /**
     * Return if a given class is annotated with @Embeddable.
     */
    public static MetadataAnnotation getEmbeddableAnnotation(MetadataClass candidateClass){
        return candidateClass.getAnnotation(JPA_EMBEDDABLE);
    }

    /**
     * Return if a given class is annotated with @Entity.
     */
    public static MetadataAnnotation getEntityAnnotation(MetadataClass candidateClass){
        return candidateClass.getAnnotation(JPA_ENTITY);
    }

    /**
     * Get a list of persistence units from the file or directory at the given
     * url. PersistenceUnits are built based on the presence of a persistence descriptor
     * *
     * @param archive The url of a jar file or directory to check
     */
    public static List<SEPersistenceUnitInfo> getPersistenceUnits(Archive archive, ClassLoader loader){
        return processPersistenceArchive(archive, loader);
    }

    /**
     * Return if a given class is annotated with @Entity.
     */
    public static MetadataAnnotation getMappedSuperclassAnnotation(MetadataClass candidateClass){
        return candidateClass.getAnnotation(JPA_MAPPED_SUPERCLASS);
    }

    /**
     * Return the @StaticMetamodel annotation on the given class.
     */
    public static MetadataAnnotation getStaticMetamodelAnnotation(MetadataClass candidateClass){
        return candidateClass.getAnnotation(JPA_STATIC_METAMODEL);
    }

    /**
     * Return if a given class is annotated with @Converter.
     */
    public static boolean isConverter(MetadataClass candidateClass) {
        return candidateClass.isAnnotationPresent(JPA_CONVERTER);
    }

    /**
     * Return if a given class is annotated with @Embeddable.
     */
    public static boolean isEmbeddable(MetadataClass candidateClass) {
        return candidateClass.isAnnotationPresent(JPA_EMBEDDABLE);
    }

    /**
     * Return if a given class is annotated with @Entity.
     */
    public static boolean isEntity(MetadataClass candidateClass){
        return candidateClass.isAnnotationPresent(JPA_ENTITY);
    }

    /**
     * Return if a given class is annotated with @StaticMetamodel.
     */
    public static boolean isStaticMetamodelClass(MetadataClass candidateClass) {
        return candidateClass.isAnnotationPresent(JPA_STATIC_METAMODEL);
    }


    /**
     * Return if a given class is annotated with @MappedSuperclass.
     */
    public static boolean isMappedSuperclass(MetadataClass candidateClass){
        return candidateClass.isAnnotationPresent(JPA_MAPPED_SUPERCLASS);
    }

    /**
     * Load the given class name with the given class loader.
     */
    public static Class loadClass(String className, ClassLoader loader, boolean throwExceptionIfNotFound, MetadataProject project) {
        Class candidateClass = null;

        try {
            candidateClass = loader.loadClass(className);
        } catch (ClassNotFoundException exc){
            if (throwExceptionIfNotFound){
                throw PersistenceUnitLoadingException.exceptionLoadingClassWhileLookingForAnnotations(className, exc);
            } else {
                AbstractSessionLog.getLog().log(AbstractSessionLog.WARNING, "persistence_unit_processor_error_loading_class", exc.getClass().getName(), exc.getLocalizedMessage() , className);
            }
        } catch (NullPointerException npe) {
            // Bug 227630: If any weavable class is not found in the temporary
            // classLoader - disable weaving
            AbstractSessionLog.getLog().log(AbstractSessionLog.WARNING, AbstractSessionLog.WEAVER, "persistence_unit_processor_error_loading_class_weaving_disabled", loader, project.getPersistenceUnitInfo().getPersistenceUnitName(), className);
            // Disable weaving (for 1->1 and many->1)only if the classLoader
            // returns a NPE on loadClass()
            project.disableWeaving();
        } catch (Exception exception){
            AbstractSessionLog.getLog().log(AbstractSessionLog.WARNING, AbstractSessionLog.WEAVER, "persistence_unit_processor_error_loading_class", exception.getClass().getName(), exception.getLocalizedMessage() , className);
        } catch (Error error){
            AbstractSessionLog.getLog().log(AbstractSessionLog.WARNING, AbstractSessionLog.WEAVER, "persistence_unit_processor_error_loading_class", error.getClass().getName(), error.getLocalizedMessage() , className);
            throw error;
        }

        return candidateClass;
    }

    /**
     * Process the Object/relational metadata from XML and annotations
     */
    public static void processORMetadata(MetadataProcessor processor, boolean throwExceptionOnFail, Mode mode) {
        if (mode == Mode.ALL || mode == Mode.COMPOSITE_MEMBER_INITIAL) {
            // DO NOT CHANGE the order of invocation of various methods.

            // 1 - Load the list of mapping files for the persistence unit. Need to
            // do this before we start processing entities as the list of entity
            // classes depend on metadata read from mapping files.
            processor.loadMappingFiles(throwExceptionOnFail);
        }

        // 2 - Process each XML entity mappings file metadata (except for
        // the actual classes themselves). This method is also responsible
        // for handling any XML merging.
        processor.processEntityMappings(mode);

        // 3 - Process the persistence unit classes (from XML and annotations)
        // and their metadata now.
        processor.processORMMetadata(mode);
    }

    /**
     * Go through the jar file for this PersistenceUnitProcessor and process any
     * XML provided in it.
     */
    public static List<SEPersistenceUnitInfo> processPersistenceArchive(Archive archive, ClassLoader loader){
        URL puRootURL = archive.getRootURL();
        try (InputStream descriptorStream = archive.getDescriptorStream()) {
            return processPersistenceXML(puRootURL, descriptorStream, loader);
          } catch (Exception e) {
            throw PersistenceUnitLoadingException.exceptionLoadingFromUrl(puRootURL.toString(), e);
        }
    }

    /**
     * Build a persistence.xml file into a SEPersistenceUnitInfo object.
     * May eventually change this to use OX mapping as well.
     */
    private static List<SEPersistenceUnitInfo> processPersistenceXML(URL baseURL, InputStream input, ClassLoader loader){
        SAXParserFactory spf = XMLHelper.createParserFactory(false);

        XMLReader xmlReader = null;
        SAXParser sp = null;
        XMLExceptionHandler xmlErrorHandler = new XMLExceptionHandler();
        // 247735 - remove the validation of XML.

        // create a SAX parser
        try {
            sp = spf.newSAXParser();
        } catch (ParserConfigurationException | SAXException exc){
            throw XMLParseException.exceptionCreatingSAXParser(baseURL, exc);
        }

        // create an XMLReader
        try {
            xmlReader = sp.getXMLReader();
            xmlReader.setErrorHandler(xmlErrorHandler);
        } catch (org.xml.sax.SAXException exc){
            throw XMLParseException.exceptionCreatingXMLReader(baseURL, exc);
        }

        PersistenceContentHandler myContentHandler = new PersistenceContentHandler();
        xmlReader.setContentHandler(myContentHandler);

        InputSource inputSource = new InputSource(input);
        try{
            xmlReader.parse(inputSource);
        } catch (IOException exc){
            throw PersistenceUnitLoadingException.exceptionProcessingPersistenceXML(baseURL, exc);
        } catch (org.xml.sax.SAXException exc){
            // XMLErrorHandler will handle SAX exceptions
        }

        // handle any parse exceptions
        XMLException xmlError = xmlErrorHandler.getXMLException();
        if (xmlError != null) {
            throw PersistenceUnitLoadingException.exceptionProcessingPersistenceXML(baseURL, xmlError);
        }

        Iterator<SEPersistenceUnitInfo> persistenceInfos = myContentHandler.getPersistenceUnits().iterator();
        while (persistenceInfos.hasNext()){
            SEPersistenceUnitInfo info = persistenceInfos.next();
            info.setPersistenceUnitRootUrl(baseURL);
            info.setClassLoader(loader);
            info.setNewTempClassLoader(loader);
        }
        return myContentHandler.getPersistenceUnits();
    }

    public static void setArchiveFactory(ArchiveFactory factory){
        ARCHIVE_FACTORY = factory;
    }

    /**
     * Build the unique persistence name by concatenating the decoded URL with the persistence unit name.
     * A decoded URL is required while persisting on a multi-bytes OS.
     * @param url
     * @param puName
     * @return String
     */
   public static String buildPersistenceUnitName(URL url, String puName){
       String fullPuName = null;
       try {
           // append the persistence unit name to the decoded URL
           fullPuName = URLDecoder.decode(url.toString(), "UTF8")+"_"+puName;
       } catch (UnsupportedEncodingException e) {
           throw PersistenceUnitLoadingException.couldNotBuildPersistenceUntiName(e,url.toString(),puName);
       }
       return fullPuName;
   }

}