View Javadoc
1   /*
2    * MIT License
3    * Copyright (c) 2006-2025 JMockit developers
4    * See LICENSE file for full license text.
5    */
6   package mockit.internal.injection.full;
7   
8   import edu.umd.cs.findbugs.annotations.NonNull;
9   import edu.umd.cs.findbugs.annotations.Nullable;
10  
11  import jakarta.persistence.EntityManager;
12  import jakarta.persistence.EntityManagerFactory;
13  import jakarta.persistence.Persistence;
14  import jakarta.persistence.PersistenceContext;
15  import jakarta.persistence.PersistenceUnit;
16  
17  import java.io.IOException;
18  import java.io.InputStream;
19  import java.lang.annotation.Annotation;
20  
21  import javax.xml.parsers.ParserConfigurationException;
22  import javax.xml.parsers.SAXParser;
23  import javax.xml.parsers.SAXParserFactory;
24  
25  import mockit.internal.injection.InjectionPoint;
26  import mockit.internal.injection.InjectionProvider;
27  import mockit.internal.injection.InjectionState;
28  
29  import org.xml.sax.Attributes;
30  import org.xml.sax.SAXException;
31  import org.xml.sax.helpers.DefaultHandler;
32  
33  /**
34   * Detects and resolves dependencies belonging to the <code>jakarta.persistence</code> API, namely
35   * <code>EntityManagerFactory</code> and <code>EntityManager</code>.
36   */
37  final class JPAJakartaDependencies {
38      static boolean isApplicable(@NonNull Class<?> dependencyType) {
39          return dependencyType == EntityManager.class || dependencyType == EntityManagerFactory.class;
40      }
41  
42      @NonNull
43      private final InjectionState injectionState;
44      @Nullable
45      private String defaultPersistenceUnitName;
46  
47      JPAJakartaDependencies(@NonNull InjectionState injectionState) {
48          this.injectionState = injectionState;
49      }
50  
51      @Nullable
52      InjectionPoint getInjectionPointIfAvailable(@NonNull Annotation jpaAnnotation) {
53          Class<? extends Annotation> annotationType = jpaAnnotation.annotationType();
54          Class<?> jpaClass;
55          String unitName;
56  
57          if (annotationType == PersistenceUnit.class) {
58              jpaClass = EntityManagerFactory.class;
59              unitName = ((PersistenceUnit) jpaAnnotation).unitName();
60          } else if (annotationType == PersistenceContext.class) {
61              jpaClass = EntityManager.class;
62              unitName = ((PersistenceContext) jpaAnnotation).unitName();
63          } else {
64              return null;
65          }
66  
67          if (unitName.isEmpty()) {
68              unitName = discoverNameOfDefaultPersistenceUnit();
69          }
70  
71          return new InjectionPoint(jpaClass, unitName, true);
72      }
73  
74      @NonNull
75      private String discoverNameOfDefaultPersistenceUnit() {
76          if (defaultPersistenceUnitName != null) {
77              return defaultPersistenceUnitName;
78          }
79  
80          defaultPersistenceUnitName = "<unknown>";
81          InputStream xmlFile = getClass().getResourceAsStream("/META-INF/persistence.xml");
82  
83          if (xmlFile != null) {
84              try {
85                  SAXParserFactory factory = SAXParserFactory.newInstance();
86                  factory.setFeature("http://xml.org/sax/features/external-general-entities", false);
87                  factory.setFeature("http://xml.org/sax/features/external-parameter-entities", false);
88                  factory.setFeature("http://apache.org/xml/features/nonvalidating/load-external-dtd", false);
89                  SAXParser parser = factory.newSAXParser();
90                  parser.parse(xmlFile, new DefaultHandler() {
91                      @Override
92                      public void startElement(String uri, String localName, String qName, Attributes attributes) {
93                          if ("persistence-unit".equals(qName)) {
94                              defaultPersistenceUnitName = attributes.getValue("name");
95                          }
96                      }
97                  });
98                  xmlFile.close();
99              } catch (ParserConfigurationException | SAXException | IOException ignore) {
100             }
101         }
102 
103         return defaultPersistenceUnitName;
104     }
105 
106     @Nullable
107     Object createAndRegisterDependency(@NonNull Class<?> dependencyType, @NonNull InjectionPoint dependencyKey,
108             @Nullable InjectionProvider injectionProvider) {
109         if (injectionProvider != null) {
110             if (dependencyType == EntityManagerFactory.class
111                     && injectionProvider.hasAnnotation(PersistenceUnit.class)) {
112                 InjectionPoint injectionPoint = createFactoryInjectionPoint(dependencyKey);
113                 return createAndRegisterEntityManagerFactory(injectionPoint);
114             }
115 
116             if (dependencyType == EntityManager.class && injectionProvider.hasAnnotation(PersistenceContext.class)) {
117                 return createAndRegisterEntityManager(dependencyKey);
118             }
119         }
120 
121         return null;
122     }
123 
124     @NonNull
125     private InjectionPoint createFactoryInjectionPoint(@NonNull InjectionPoint injectionPoint) {
126         String persistenceUnitName = getNameOfPersistentUnit(injectionPoint.name);
127         return new InjectionPoint(EntityManagerFactory.class, persistenceUnitName, injectionPoint.qualified);
128     }
129 
130     @NonNull
131     private String getNameOfPersistentUnit(@Nullable String injectionPointName) {
132         return injectionPointName != null && !injectionPointName.isEmpty() ? injectionPointName
133                 : discoverNameOfDefaultPersistenceUnit();
134     }
135 
136     @NonNull
137     private static EntityManagerFactory createAndRegisterEntityManagerFactory(@NonNull InjectionPoint injectionPoint) {
138         String persistenceUnitName = injectionPoint.name;
139         EntityManagerFactory emFactory = Persistence.createEntityManagerFactory(persistenceUnitName);
140         InjectionState.saveGlobalDependency(injectionPoint, emFactory);
141         return emFactory;
142     }
143 
144     @NonNull
145     private EntityManager createAndRegisterEntityManager(@NonNull InjectionPoint injectionPoint) {
146         InjectionPoint emFactoryKey = createFactoryInjectionPoint(injectionPoint);
147         EntityManagerFactory emFactory = InjectionState.getGlobalDependency(emFactoryKey);
148 
149         if (emFactory == null) {
150             emFactory = createAndRegisterEntityManagerFactory(emFactoryKey);
151         }
152 
153         EntityManager entityManager = emFactory.createEntityManager();
154         injectionState.saveInstantiatedDependency(injectionPoint, entityManager);
155         return entityManager;
156     }
157 }