001: /*
002: * JFox - The most lightweight Java EE Application Server!
003: * more details please visit http://www.huihoo.org/jfox or http://www.jfox.org.cn.
004: *
005: * JFox is licenced and re-distributable under GNU LGPL.
006: */
007: package org.jfox.entity;
008:
009: import java.net.URL;
010: import java.util.ArrayList;
011: import java.util.Arrays;
012: import java.util.Collection;
013: import java.util.Collections;
014: import java.util.HashMap;
015: import java.util.HashSet;
016: import java.util.Iterator;
017: import java.util.List;
018: import java.util.Map;
019: import java.util.Set;
020: import javax.persistence.EntityManagerFactory;
021: import javax.persistence.EntityTransaction;
022: import javax.persistence.NamedNativeQueries;
023: import javax.persistence.NamedNativeQuery;
024: import javax.persistence.PersistenceException;
025: import javax.persistence.QueryHint;
026: import javax.sql.DataSource;
027: import javax.transaction.TransactionManager;
028:
029: import org.apache.log4j.Logger;
030: import org.enhydra.jdbc.pool.StandardXAPoolDataSource;
031: import org.enhydra.jdbc.standard.StandardXADataSource;
032: import org.jfox.ejb3.naming.JNDIContextHelper;
033: import org.jfox.ejb3.transaction.JTATransactionManager;
034: import org.jfox.entity.cache.CacheConfig;
035: import org.jfox.framework.annotation.Service;
036: import org.jfox.framework.component.ASMClassLoader;
037: import org.jfox.framework.component.ActiveComponent;
038: import org.jfox.framework.component.Component;
039: import org.jfox.framework.component.ComponentContext;
040: import org.jfox.framework.component.ComponentInitialization;
041: import org.jfox.framework.component.ComponentUnregistration;
042: import org.jfox.framework.component.Module;
043: import org.jfox.framework.event.ModuleEvent;
044: import org.jfox.framework.event.ModuleListener;
045: import org.jfox.framework.event.ModuleLoadingEvent;
046: import org.jfox.framework.event.ModuleUnloadedEvent;
047: import org.jfox.util.XMLUtils;
048: import org.w3c.dom.Document;
049: import org.w3c.dom.Element;
050:
051: /**
052: * @author <a href="mailto:jfox.young@gmail.com">Young Yang</a>
053: */
054: @Service(id="EntityManagerFactoryBuilder",active=true,singleton=true,priority=Integer.MIN_VALUE)
055: public class EntityManagerFactoryBuilderImpl implements
056: EntityManagerFactoryBuilder, Component,
057: ComponentInitialization, ComponentUnregistration,
058: ModuleListener, ActiveComponent {
059:
060: protected static Logger logger = Logger
061: .getLogger(EntityManagerFactoryBuilderImpl.class);
062:
063: private final static String PERSISTENCE_CONFIG_FILE = "META-INF/persistence.xml";
064:
065: //TransactionManager
066: private TransactionManager transactionManager = null;
067:
068: /**
069: * Entity Manager Factory Map
070: * unit name => EntityManagerFactoryImpl
071: */
072: private static Map<String, EntityManagerFactoryImpl> emFactoryMap = new HashMap<String, EntityManagerFactoryImpl>();
073: /**
074: * query name => query template
075: */
076: private static Map<String, NamedSQLTemplate> namedSQLTemplates = new HashMap<String, NamedSQLTemplate>();
077:
078: private Document xmlDocument = null;
079:
080: private static boolean inited = false;
081: private boolean containerManaged = true;
082:
083: private EntityTransaction entityTransaction = null;
084:
085: public static final String DEFAULT_UNITNAME = "default";
086: public static final String QUERY_HINT_KEY_FORM_CAHCE_PREFIX = "cache.";
087: public static final String QUERY_HINT_KEY_FOR_JDBC_COMPATIBLE = "jdbc.compatible";
088:
089: public EntityManagerFactoryBuilderImpl() {
090:
091: }
092:
093: /**
094: * 容器外�行时,通过 Persistence.createEntityManagerFactory 调用时,会使用该方法
095: * 需��始化 EntityManagerFactoryBuilderImpl,注册所有 NamedQuery
096: *
097: * @param name unit name
098: */
099: public static EntityManagerFactoryImpl getEntityManagerFactoryByName(
100: String name) {
101: if (!inited) { // 没有�始化,是容器外调用,如果是容器内调用,应该已��始化
102: EntityManagerFactoryBuilderImpl entityManagerFactoryBuilder = new EntityManagerFactoryBuilderImpl();
103: entityManagerFactoryBuilder.containerManaged = false;
104: // �始化所有的 EntityManagerFactory
105: entityManagerFactoryBuilder.initEntityManagerFactories();
106: ASMClassLoader asmClassLoader = new ASMClassLoader(
107: entityManagerFactoryBuilder.getClass()
108: .getClassLoader());
109: Set<Class> namedQueryClasses = new HashSet<Class>();
110: namedQueryClasses.addAll(Arrays.asList(asmClassLoader
111: .findClassAnnotatedWith(NamedNativeQueries.class)));
112: namedQueryClasses.addAll(Arrays.asList(asmClassLoader
113: .findClassAnnotatedWith(NamedNativeQuery.class)));
114: entityManagerFactoryBuilder
115: .registerNamedQueriesByClasses(namedQueryClasses
116: .toArray(new Class[namedQueryClasses.size()]));
117: inited = true;
118: }
119: return emFactoryMap.get(name);
120: }
121:
122: public static EntityManagerFactory getDefaultEntityManagerFactory() {
123: if (emFactoryMap.size() != 1) {
124: throw new PersistenceException(
125: "More than one unitName, can not decide default!");
126: }
127: return emFactoryMap.values().toArray(
128: new EntityManagerFactory[1])[0];
129: }
130:
131: public static Collection<EntityManagerFactoryImpl> getEntityManagerFactories() {
132: return Collections
133: .unmodifiableCollection(emFactoryMap.values());
134: }
135:
136: /**
137: * 使用 @Resource 未指定 name 注入
138: */
139: public static DataSource getDefaultDataSource() {
140: return ((EntityManagerFactoryImpl) getDefaultEntityManagerFactory())
141: .getDataSource();
142: }
143:
144: /**
145: * 使用 @Resource 指定 name 注入
146: *
147: * @param unitName unit name, same as @resource name
148: */
149: public static DataSource getDataSourceByUnitName(String unitName) {
150: EntityManagerFactoryImpl emf = emFactoryMap.get(unitName);
151: if (emf == null) {
152: throw new PersistenceException(
153: "Can not find DataSource with unitName: "
154: + unitName);
155: } else {
156: return emf.getDataSource();
157: }
158: }
159:
160: /**
161: * get data source by Mapped Name, if inject by @PersistenceContext(mappedName="")
162: *
163: * @param mappedName mapped name, same as jndi name
164: */
165: public static DataSource getDataSourceByMappedName(String mappedName) {
166: for (EntityManagerFactory emf : emFactoryMap.values()) {
167: StandardXAPoolDataSource dataSource = (StandardXAPoolDataSource) (((EntityManagerFactoryImpl) emf)
168: .getDataSource());
169: if ((dataSource.getDataSourceName().equals(mappedName))) {
170: return dataSource;
171: }
172: }
173: throw new PersistenceException(
174: "Can not find DataSource with mappedName: "
175: + mappedName);
176: }
177:
178: public static Collection<NamedSQLTemplate> getNamedSQLTemplates() {
179: return Collections.unmodifiableCollection(namedSQLTemplates
180: .values());
181: }
182:
183: //get CacheConfig by unit & cacheConfigName
184: public static CacheConfig getCacheConfig(String unitName) {
185: EntityManagerFactoryImpl emf = getEntityManagerFactoryByName(unitName);
186: if (emf != null) {
187: return emf.getCacheConfig();
188: } else {
189: return null;
190: }
191: }
192:
193: public void postContruct(ComponentContext componentContext) {
194: inited = true;
195: }
196:
197: public void postInject() {
198: containerManaged = true;
199: initEntityManagerFactories();
200: }
201:
202: public void postUnregister() {
203:
204: for (EntityManagerFactory emFactory : emFactoryMap.values()) {
205: // will close data source
206: emFactory.close();
207: }
208: emFactoryMap.clear();
209: namedSQLTemplates.clear();
210: }
211:
212: public boolean preUnregister(ComponentContext context) {
213: return true;
214: }
215:
216: public boolean isContainerManaged() {
217: return containerManaged;
218: }
219:
220: public void moduleChanged(ModuleEvent moduleEvent) {
221:
222: if (moduleEvent instanceof ModuleLoadingEvent) {
223: Module module = moduleEvent.getModule();
224: Class[] namedQueriesClasses = module.getModuleClassLoader()
225: .findClassAnnotatedWith(NamedNativeQueries.class);
226: registerNamedQueriesByClasses(namedQueriesClasses);
227: }
228:
229: if (moduleEvent instanceof ModuleUnloadedEvent) {
230: Module module = moduleEvent.getModule();
231: Iterator<Map.Entry<String, NamedSQLTemplate>> it = namedSQLTemplates
232: .entrySet().iterator();
233: while (it.hasNext()) {
234: Map.Entry<String, NamedSQLTemplate> entry = it.next();
235: NamedSQLTemplate sqlTemplate = entry.getValue();
236: // 注销所在模�的 NamedSQLTemplate
237: if (sqlTemplate.getDefinedClass().getClassLoader() == module
238: .getModuleClassLoader()) {
239: logger
240: .info("Unregister Named Query defined in class: "
241: + sqlTemplate.getDefinedClass()
242: .getName()
243: + ", template SQL: "
244: + sqlTemplate.getTemplateSQL());
245: it.remove();
246: }
247: }
248: }
249:
250: }
251:
252: private void initEntityManagerFactories() {
253: URL url = this .getClass().getClassLoader().getResource(
254: PERSISTENCE_CONFIG_FILE);
255: if (url == null) {
256: logger.warn("Can not found persistence config file: "
257: + PERSISTENCE_CONFIG_FILE);
258: return;
259: }
260: logger.info("Initializing EntityManagers use: "
261: + PERSISTENCE_CONFIG_FILE);
262: transactionManager = JTATransactionManager.getIntsance();
263: // �始化 EntityTransaction
264: entityTransaction = new EntityTransactionImpl(
265: transactionManager);
266:
267: try {
268: xmlDocument = XMLUtils.loadDocument(url);
269: Element rootElement = xmlDocument.getDocumentElement();
270: List<Element> persistenceUnits = XMLUtils
271: .getElementsByTagName(rootElement,
272: "persistence-unit");
273: for (Element element : persistenceUnits) {
274: EntityManagerFactoryImpl emFactory = createEntityManagerFactory(element);
275: emFactoryMap.put(emFactory.getUnitName(), emFactory);
276: // �有容器管�的时候,�注册到 JNDi
277: if (isContainerManaged()) {
278: //注入的应该是:jta-data-source
279: JNDIContextHelper.getInitalContext().bind(
280: ((StandardXAPoolDataSource) emFactory
281: .getDataSource())
282: .getDataSourceName(), emFactory);
283: }
284: }
285: } catch (Exception e) {
286: logger.error("Create document for "
287: + PERSISTENCE_CONFIG_FILE + " error!", e);
288: }
289: }
290:
291: public EntityTransaction getEntityTransaction() {
292: return entityTransaction;
293: }
294:
295: private EntityManagerFactoryImpl createEntityManagerFactory(
296: Element element) throws Exception {
297: String unitName = XMLUtils.getAtrributeValue(element, "name");
298: String jndiName = "java:/" + unitName;
299: String jtaDataSource = XMLUtils.getChildElementValueByTagName(
300: element, "jta-data-source");
301: if (jtaDataSource != null && !jtaDataSource.trim().equals("")) {
302: jndiName = jtaDataSource;
303: }
304:
305: Map<String, String> properties = new HashMap<String, String>();
306: List<Element> propertysElements = XMLUtils
307: .getElementsByTagName(element, "property");
308: for (Element propElement : propertysElements) {
309: properties.put(XMLUtils.getAtrributeValue(propElement,
310: "name"), XMLUtils.getAtrributeValue(propElement,
311: "value"));
312: }
313:
314: StandardXADataSource sxds = new StandardXADataSource();
315: StandardXAPoolDataSource sxpds = new StandardXAPoolDataSource();
316: sxpds.setJdbcTestStmt("select 1");
317: // check connection after checkOut from xapool, if closed, expire it and reconnect
318: sxpds.setCheckLevelObject(4);
319: sxpds.setDataSourceName(jndiName);
320:
321: // cache config
322: CacheConfig cacheConfig = null;
323: for (Map.Entry<String, String> entry : properties.entrySet()) {
324: String name = entry.getKey();
325: String value = entry.getValue();
326: if (name.equalsIgnoreCase("driver")) {
327: sxds.setDriverName(value);
328: } else if (name.equalsIgnoreCase("url")) {
329: sxds.setUrl(value);
330: } else if (name.equalsIgnoreCase("username")) {
331: sxpds.setUser(value);
332: sxds.setUser(value);
333: } else if (name.equalsIgnoreCase("password")) {
334: sxds.setPassword(value);
335: sxpds.setPassword(value);
336: } else if (name.equals("checkLevelObject")) {
337: sxpds.setCheckLevelObject(Integer.parseInt(value));
338: } else if (name.equalsIgnoreCase("minSize")) {
339: sxpds.setMinSize(Integer.parseInt(value));
340: } else if (name.equalsIgnoreCase("maxSize")) {
341: sxpds.setMaxSize(Integer.parseInt(value));
342: } else if (name.equalsIgnoreCase("lifeTime")) {
343: sxpds.setLifeTime(Long.parseLong(value));
344: } else if (name.equalsIgnoreCase("sleepTime")) {
345: sxpds.setSleepTime(Long.parseLong(value));
346: } else if (name.equalsIgnoreCase("deadLockRetryWait")) {
347: sxpds.setDeadLockRetryWait(Long.parseLong(value));
348: } else if (name.equalsIgnoreCase("deadLockMaxWait")) {
349: sxpds.setDeadLockMaxWait(Long.parseLong(value));
350: } else if (name
351: .startsWith(QUERY_HINT_KEY_FORM_CAHCE_PREFIX)) {
352: // construct cache config
353: if (cacheConfig == null) {
354: cacheConfig = new CacheConfig(unitName);
355: }
356: String property = name
357: .substring(name.lastIndexOf(".") + 1);
358: if (property.equalsIgnoreCase("TTL")) {
359: cacheConfig.setTTL(Long.parseLong(value));
360: } else if (property.equalsIgnoreCase("algorithm")) {
361: if (value.equalsIgnoreCase("LFU")) {
362: cacheConfig
363: .setAlgorithm(CacheConfig.Algorithm.LFU);
364: } else if (value.equalsIgnoreCase("FIFO")) {
365: cacheConfig
366: .setAlgorithm(CacheConfig.Algorithm.FIFO);
367: } else {
368: cacheConfig
369: .setAlgorithm(CacheConfig.Algorithm.LRU);
370: }
371: } else if (property.equalsIgnoreCase("maxIdleTime")) {
372: cacheConfig.setMaxIdleTime(Long.parseLong(value));
373: } else if (property.equalsIgnoreCase("maxSize")) {
374: cacheConfig.setMaxSize(Integer.parseInt(value));
375: } else if (property.equalsIgnoreCase("maxMemorySize")) {
376: cacheConfig.setMaxMemorySize(Long.parseLong(value));
377: } else {
378: logger.warn("Illegal JPA cache property name: "
379: + name);
380: }
381: } else {
382: logger
383: .warn("Illegal JPA persistence.xml property name: "
384: + name);
385: }
386: }
387: sxpds.setTransactionManager(transactionManager);
388: sxpds.setDataSource(sxds);
389:
390: // create EntityManagerFactory
391: return new EntityManagerFactoryImpl(unitName, jndiName, this ,
392: sxpds, cacheConfig);
393: }
394:
395: private void registerNamedQueriesByClasses(Class[] classes) {
396: for (Class<?> beanClass : classes) {
397: List<String> queryNames = new ArrayList<String>();
398: if (beanClass.isAnnotationPresent(NamedNativeQueries.class)) {
399: NamedNativeQueries namedNativeQueries = beanClass
400: .getAnnotation(NamedNativeQueries.class);
401: for (NamedNativeQuery namedNativeQuery : namedNativeQueries
402: .value()) {
403: this
404: .registerNamedQuery(namedNativeQuery,
405: beanClass);
406: queryNames.add(namedNativeQuery.name());
407: }
408: }
409: if (beanClass.isAnnotationPresent(NamedNativeQuery.class)) {
410: NamedNativeQuery namedNativeQuery = beanClass
411: .getAnnotation(NamedNativeQuery.class);
412: this .registerNamedQuery(namedNativeQuery, beanClass);
413: queryNames.add(namedNativeQuery.name());
414: }
415: logger.info("Register NamedQuery for Class: "
416: + beanClass.getName()
417: + ", "
418: + Arrays.toString(queryNames
419: .toArray(new String[queryNames.size()])));
420: }
421: }
422:
423: public void registerNamedQuery(NamedNativeQuery namedNativeQuery,
424: Class<?> definedClass) {
425: if (namedSQLTemplates.containsKey(namedNativeQuery.name())) {
426: logger.warn("NamedQuery "
427: + namedNativeQuery.name()
428: + " has registered by "
429: + namedSQLTemplates.get(namedNativeQuery.name())
430: .getDefinedClass() + ".");
431: } else {
432: NamedSQLTemplate sqlTemplate = new NamedSQLTemplate(
433: namedNativeQuery, definedClass);
434: QueryHint[] hints = namedNativeQuery.hints();
435: if (hints.length > 0) {
436: // 检查 jdbc.compatible
437: for (QueryHint hint : hints) {
438: if (hint.name().equals(
439: QUERY_HINT_KEY_FOR_JDBC_COMPATIBLE)) {
440: String compatibleDatabaseTypes = hint.value();
441: for (String compatibleDbType : compatibleDatabaseTypes
442: .split(",")) {
443: //query_name = getUserInfo.MYSQL
444: compatibleDbType = compatibleDbType.trim();
445: if (compatibleDbType.length() > 0) {
446: namedSQLTemplates.put(sqlTemplate
447: .getName()
448: + "."
449: + compatibleDbType.trim()
450: .toUpperCase(),
451: sqlTemplate);
452: }
453: }
454: } else {
455: namedSQLTemplates.put(sqlTemplate.getName(),
456: sqlTemplate);
457: }
458: }
459: } else {
460: namedSQLTemplates.put(sqlTemplate.getName(),
461: sqlTemplate);
462: }
463: }
464: }
465:
466: public NamedSQLTemplate getNamedQuery(String name, String dbType) {
467: NamedSQLTemplate sqlTemplate = namedSQLTemplates.get(name + "."
468: + dbType.toUpperCase());
469: if (sqlTemplate == null) {
470: sqlTemplate = namedSQLTemplates.get(name);
471: }
472: return sqlTemplate;
473: }
474:
475: public Document getPersistenceXMLDocument() {
476: return xmlDocument;
477: }
478:
479: public static void main(String[] args) {
480: EntityManagerFactory entityManagerFactory = EntityManagerFactoryBuilderImpl
481: .getEntityManagerFactoryByName("DefaultMysqlDS");
482: System.out.println(entityManagerFactory);
483: }
484: }
|