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