View Javadoc
1   /**
2    * copyright by Grandsoft Company Limited.  
3    * 2011-12-31
4    */
5   package gboat2.base.dao;
6   
7   import gboat2.base.core.dao.IBaseDAO;
8   import gboat2.base.dao.aspect.GboatSessionFactoryAspect;
9   import gboat2.base.dao.util.GBoatDaoClassLoader;
10  
11  import java.io.FileInputStream;
12  import java.io.IOException;
13  import java.net.URL;
14  import java.util.ArrayList;
15  import java.util.Collection;
16  import java.util.HashMap;
17  import java.util.List;
18  import java.util.Map;
19  import java.util.Map.Entry;
20  import java.util.Set;
21  import java.util.zip.ZipEntry;
22  import java.util.zip.ZipInputStream;
23  
24  import javax.persistence.Entity;
25  
26  import org.apache.commons.collections.CollectionUtils;
27  import org.apache.commons.collections.MapUtils;
28  import org.apache.commons.io.IOUtils;
29  import org.apache.commons.lang3.StringUtils;
30  import org.hibernate.SessionFactory;
31  import org.osgi.framework.Bundle;
32  import org.osgi.framework.BundleActivator;
33  import org.osgi.framework.BundleContext;
34  import org.osgi.framework.BundleEvent;
35  import org.osgi.framework.BundleException;
36  import org.osgi.framework.BundleListener;
37  import org.slf4j.Logger;
38  import org.slf4j.LoggerFactory;
39  import org.springframework.beans.factory.NoSuchBeanDefinitionException;
40  import org.springframework.beans.factory.NoUniqueBeanDefinitionException;
41  import org.springframework.context.ApplicationContext;
42  import org.springframework.context.ApplicationContextAware;
43  import org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean;
44  
45  /**
46   * bundle model对象注册工具类
47   * @author liuliang,lysming
48   * 2011-12-31
49   */
50  public class Activator implements BundleActivator, BundleListener, ApplicationContextAware {
51  
52  	private static final Logger logger = LoggerFactory.getLogger(Activator.class);
53  
54  	private static final String MODEL_PACKAGE_HEADER = "DynamicImport-Model";
55  
56  	/** 需要注册 POJO 的 Bundle */
57  	private static final List<Bundle> bundlesToRegistPo = new ArrayList<Bundle>();
58  
59  	/** 已经完成 POJO 注册的 Bundle, key 为 Bundle 的 symbolicName, value 为 Bundle 实例 */
60  	private static Map<String, Bundle> bundlesCache = new HashMap<String, Bundle>();
61  
62  	private static Collection<AnnotationSessionFactoryBean> sessionFactoryBeans;
63  	private GboatSessionFactoryAspect sessionFactoryAspect;
64  
65  	/** key 为 Bundle 的 symbolicName, value 为该 Bundle 中所有的 VO、 PO 类 */
66  	private static Map<String, List<Class<?>>> annotatedClassesMap = new HashMap<String, List<Class<?>>>();
67  	
68  	/** 保存所有要注册到 SessionFacotry 的类 */
69  	private static List<Class<?>> registClasses = new ArrayList<Class<?>>();
70  
71  	private static GBoatDaoClassLoader gboatLoader = null;
72  	
73  	private IBaseDAO baseDAO;
74  
75      @Override
76      public void setApplicationContext(ApplicationContext applicationContext) {
77          if (sessionFactoryBeans == null) {
78              Map<String, AnnotationSessionFactoryBean> sessionFactoryBeanMap = applicationContext.getBeansOfType(AnnotationSessionFactoryBean.class);
79              if(MapUtils.isEmpty(sessionFactoryBeanMap))
80                  throw new NoSuchBeanDefinitionException(AnnotationSessionFactoryBean.class, "无法将 Bundle 中的 POJO 注册到 Hibernate SessionFactory。");
81              sessionFactoryBeans = sessionFactoryBeanMap.values();
82              
83              Map<String, GboatSessionFactoryAspect> sessionFactoryAspectMap = applicationContext.getBeansOfType(GboatSessionFactoryAspect.class);
84              if(MapUtils.isNotEmpty(sessionFactoryAspectMap)) {
85                  if(sessionFactoryAspectMap.size() > 1)
86                      throw new NoUniqueBeanDefinitionException(GboatSessionFactoryAspect.class, sessionFactoryAspectMap.keySet());
87                  
88                  sessionFactoryAspect = sessionFactoryAspectMap.values().iterator().next();
89              }
90  
91              if (bundlesToRegistPo.size() > 0) {
92                  logger.debug("call registerPOJO by setApplicationContext");
93                  registerPOJO();
94              }
95          }
96      }
97  
98  	@Override
99  	public void start(BundleContext context) throws Exception {
100 		//registe gboatLoader 
101 		if (gboatLoader == null) {
102 		    // ID=0 的 Bundle 为系统 Bundle
103 			gboatLoader = GBoatDaoClassLoader.createBundleClassLoaderFor(context.getBundle(0), this.getClass().getClassLoader());
104 			if (gboatLoader == null) {
105 				System.err.println("Error while start dao bundle, init ClassLoader failed. System will exit.");
106 				System.exit(0);
107 			}
108 		}
109 
110 		Bundle[] bundles = context.getBundles();
111 		StringBuffer symbolicNames = new StringBuffer();
112 		for (Bundle bundle : bundles) {
113 			if (bundle.getState() == Bundle.ACTIVE) {
114 				if (!StringUtils.isEmpty(bundle.getHeaders().get(MODEL_PACKAGE_HEADER))) {
115 				    symbolicNames.append("\r\n    ").append(bundle.getSymbolicName());
116 					bundlesToRegistPo.add(bundle);
117 				}
118 			}
119 		}
120         logger.info("the pojo bundles is already started:{}" , symbolicNames);
121 
122 		//注册PO对象
123 		if (CollectionUtils.isNotEmpty(bundlesToRegistPo) && CollectionUtils.isNotEmpty(sessionFactoryBeans)) {
124 			logger.debug("call registerPOJO by start");
125 			registerPOJO();
126 		}
127 		
128 		//registe listener 
129 		logger.info("register bundle listener for monitor PO.");
130 		context.addBundleListener(this);
131 	}
132 
133 	@Override
134 	public void bundleChanged(BundleEvent event) {
135 		Bundle bundle = event.getBundle();
136 		synchronized (bundlesToRegistPo) {
137 			if ((event.getType() & BundleEvent.STARTED) != 0) { // 启动 Bundle 时
138 				//start:添加PO对象到处理列表中
139 				logger.info("STARTED => {}({})", bundle, bundle.getLastModified());
140 				if (bundlesCache.containsKey(bundle.getSymbolicName())) {
141                     // 检查缓存,如果bundle发生变更,unintall旧的bundle
142 					Bundle oldBundle = bundlesCache.get(bundle.getSymbolicName());
143 					logger.info("cached old version is : bundleId={}, state={}", oldBundle.getBundleId(), oldBundle.getState());
144 					if (oldBundle.getBundleId() != bundle.getBundleId()) {
145 						try {
146 							oldBundle.uninstall();
147 						} catch (BundleException e) {
148 							logger.warn("error to uninstall old version bundle", e);
149 						} catch (IllegalStateException e) {
150 							logger.warn(e.getMessage());
151 						}
152 					}
153 					bundlesCache.remove(bundle.getSymbolicName());
154 				}
155 				
156 				if (!StringUtils.isEmpty((String) bundle.getHeaders().get(MODEL_PACKAGE_HEADER))) {
157 					bundlesToRegistPo.add(bundle);
158 				}
159 
160 				//重新注册PO对象
161 				if (CollectionUtils.isNotEmpty(bundlesToRegistPo) && CollectionUtils.isNotEmpty(sessionFactoryBeans)) {
162 					logger.debug("call registerPOJO by bundleChanged");
163 					registerPOJO();
164 				}
165 			} else if ((event.getType() & BundleEvent.STOPPED) != 0) { // 停止 Bundle 时
166 				logger.info("STOPED => {}({})", bundle, bundle.getLastModified());
167 				bundlesToRegistPo.remove(bundle);
168 			}
169 		}
170 	}
171 
172 	/**
173 	 * 注册自定义po对象
174 	 */
175 	private synchronized void registerPOJO() {
176 		while (bundlesToRegistPo.size() > 0) {
177 			parseBundleClasses();
178 		}
179 
180 		try {
181 			if (annotatedClassesMap.size() > 0) {
182 				// 重新生成 sessionFactory
183 				logger.info("rebuild session factory");
184 
185                 if(sessionFactoryAspect == null) {
186                     if(sessionFactoryBeans.size() > 1)
187                         throw new NoUniqueBeanDefinitionException(AnnotationSessionFactoryBean.class, sessionFactoryBeans.size(), "没有配置 "
188                                 + GboatSessionFactoryAspect.class + " 切面,无法确定 POJO 与 SessionFactory 之间的对应关系,因而不支持多个 SessionFactory。");
189                     
190                     // 因为在 setApplicationContext(ApplicationContext applicationContext) 方法中已经判断了 sessionFactoryBeans 是否为空,
191                     // 所以此处无需再对该集合进行非空判断,直接取值即可
192                     AnnotationSessionFactoryBean bean = sessionFactoryBeans.iterator().next();
193                     bean.destroy();
194                     bean.setBeanClassLoader(gboatLoader);
195                     bean.setAnnotatedClasses(registClasses.toArray(new Class[0]));
196                     bean.afterPropertiesSet();
197                     baseDAO.setDefaultSessionFactory(bean.getObject());
198                 } else {
199                     // 设置 POJO 与 SessionFactory 之间的映射关系
200                     Map<SessionFactory, List<Class<?>>> sessionFactoryPojoMap = new HashMap<SessionFactory, List<Class<?>>>();
201                     // 方法名、类名的正则表达式与 SessionFactory 之间的映射关系
202                     Set<Entry<String, SessionFactory>> entrySet = sessionFactoryAspect.getTargetSessionFactorys().entrySet();
203                     SessionFactory defaultSessionFactory = this.baseDAO.getDefaultSessionFactory();
204                     SessionFactory matchSessionFactory = null;
205                     List<Class<?>> tempList;
206                     for(Class<?> clazz : registClasses) {
207                         matchSessionFactory = null; // 在每次循环的开始将 matchSessionFactory 重置为 null
208                         for (Entry<String, SessionFactory> entry : entrySet) {
209                             if(clazz.getName().matches(entry.getKey())) {
210                                 matchSessionFactory = entry.getValue();
211                                 break;
212                             }
213                         }
214                         
215                         if(matchSessionFactory == null) {
216                             matchSessionFactory = defaultSessionFactory;
217                         }
218 
219                         tempList = sessionFactoryPojoMap.get(matchSessionFactory);
220                         if(tempList == null){
221                             tempList = new ArrayList<Class<?>>();
222                             sessionFactoryPojoMap.put(matchSessionFactory, tempList);
223                         }
224                         tempList.add(clazz);
225                     }
226                     
227                     // 根据 POJO 与 SessionFactory 之间的映射关系,将 POJO 分别注册到不同的 SessionFactory 中
228                     SessionFactory oldSessionFactory = null;
229                     SessionFactory newSessionFactory = null;
230                     boolean isDefault = false;
231                     for(AnnotationSessionFactoryBean bean : sessionFactoryBeans) {
232                         oldSessionFactory = bean.getObject();
233                         isDefault = oldSessionFactory.equals(baseDAO.getDefaultSessionFactory());
234                         List<Class<?>> pojoList = sessionFactoryPojoMap.get(oldSessionFactory);
235                         if(CollectionUtils.isNotEmpty(pojoList)){
236                             bean.destroy();
237                             bean.setBeanClassLoader(gboatLoader);
238                             bean.setAnnotatedClasses(pojoList.toArray(new Class[0]));
239                             bean.afterPropertiesSet();
240                             
241                             // 经过前面的操作之后,此处的 bean.getObject() 和之前的 sessionFactory 已经不是同一个实例了
242                             newSessionFactory = bean.getObject();
243                             baseDAO.addSessionFactory(newSessionFactory);
244                             if (isDefault) {
245                                 // 设置默认的 SessionFactory
246                                 baseDAO.setDefaultSessionFactory(newSessionFactory);
247                             }
248                             
249                             // 将切面中设置的 SessionFactory 也对应更新
250                             for (Entry<String, SessionFactory> entry : entrySet) {
251                                 if(oldSessionFactory.equals(entry.getValue())) {
252                                     entry.setValue(newSessionFactory);
253                                 }
254                             }
255                         }
256                     }
257                 }
258                 // logger.debug("classes registered : " + registClasses);
259 			}
260 		} catch (Exception e) {
261 		    logger.trace("Gboat2 DAO Bundle 注册 POJO 失败。", e);
262 		}
263 	}
264 
265 	private void parseBundleClasses() {
266 		String[] importItems;
267 		List<String> importClassNames;
268 		// 处理本次包含注册 po 的 bundle
269 		synchronized (bundlesToRegistPo) {
270 			List<Class<?>> importClasses = null;
271 			String importPath; // PO、VO 所在包的包名
272 			for (Bundle bundle : bundlesToRegistPo) {
273 				if (bundle == null)
274 					continue;
275 				
276 				logger.debug("register po for {}", bundle.getSymbolicName());
277 				importClasses = new ArrayList<Class<?>>();
278 				if(bundlesCache.containsKey(bundle.getSymbolicName())){
279 					//已经注册过的,清理之前添加的类
280 					List<Class<?>> registedClasses = annotatedClassesMap.remove(bundle.getSymbolicName());
281 					Class<?> clazz = null;
282 					while(registedClasses != null && !registedClasses.isEmpty()){
283 					    clazz = registedClasses.remove(0);
284 						gboatLoader.remove(clazz.getName());
285 					}
286 				}
287 				bundlesCache.put(bundle.getSymbolicName(), bundle);
288 
289 				importPath = (String) bundle.getHeaders().get(MODEL_PACKAGE_HEADER);
290 				if (StringUtils.isEmpty(importPath)) { //path为空添加空集合
291 					annotatedClassesMap.put(bundle.getSymbolicName(), importClasses);
292 					continue;
293 				} else {
294 					// 处理import
295 					logger.debug("dynamic import models : {}", importPath);
296 					importItems = importPath.split(",");
297 					for (String importItem : importItems) {
298 						//判断 import 的是 class 还是 package
299 						try {
300 							Class<?> clazz = bundle.loadClass(importItem);
301 							if (clazz != null) {
302 								logger.debug("{} is a class", importItem);
303 								importClasses.add(clazz);
304 								continue;
305 							}
306 						} catch (ClassNotFoundException e1) {
307 							logger.warn(e1.getMessage());
308 							importClassNames = getClasses(importItem, bundle);
309 							if (importClassNames.size() > 0) {
310 								logger.debug("{} is a package, find classes in it is : {}", importItem, importClassNames);
311 								for (String className : importClassNames) {
312 									try {
313 										//将load到的class放入List中
314 										importClasses.add(bundle.loadClass(className));
315 									} catch (ClassNotFoundException e) {
316 										logger.debug(e.getMessage(), e.getCause());
317 									}
318 								}
319 							}
320 						}
321 					}
322 
323 					//处理此bundle需要加载的类
324 					annotatedClassesMap.put(bundle.getSymbolicName(), importClasses);
325 				}
326 				
327 				for (Class<?> dealClass : importClasses) {
328 					if (isEntity(dealClass)) {
329 						registClasses.add(dealClass);
330 					}
331 					//将对应关系放入自定义classloader中
332 					gboatLoader.add(dealClass.getName(), bundle);
333 				}
334 			}
335 			bundlesToRegistPo.clear();
336 		}
337 	}
338 
339 	/**   
340 	 * 获取 Bundle 指定包及其所有子包下所有类的名称(类的全限定名)
341 	 * @param pack   要扫描的包
342 	 * @param bundle bundle
343 	 * @return   List
344 	 */
345 	public static List<String> getClasses(String pack, Bundle bundle) {
346 		// class类的集合    
347 		List<String> classes = new ArrayList<String>();
348 		// 获取包的名字 并进行替换    
349 		String packageName = pack;
350 		String packageDirName = packageName.replace('.', '/');
351 		try {
352             URL url = bundle.getResource(packageDirName);
353             if (url == null) // 防止配置的包不存在
354                 return classes;
355 
356 			if (!"bundle".equals(url.getProtocol())) // 协议的名称不为 Bundle,直接返回
357 			    return classes;
358 			
359 			// 获取包的物理路径   
360 			String filePath = bundle.getLocation().replaceFirst("file:(/|\\\\)", "");
361 			// 以文件的方式扫描整个包下的文件 并添加到集合中    
362 			ZipInputStream in = null;
363 			try {
364 				in = new ZipInputStream(new FileInputStream(filePath));
365 				ZipEntry entry = null;
366 				String name;
367 				while ((entry = in.getNextEntry()) != null) {
368 					if (!entry.isDirectory()) {
369 						name = entry.getName();
370 						if (name.startsWith(packageDirName) && name.endsWith(".class")) {
371 							classes.add(name.substring(0, name.indexOf(".class")).replaceAll("/", "."));
372 						}
373 					}
374 				}
375 			} catch (IOException e) {
376 				throw new ClassNotFoundException("error : " + packageName + " the jar file [" + bundle + "] can't find");
377 			} finally {
378 			    IOUtils.closeQuietly(in);
379 			}
380 		} catch (Exception e) {
381 		    logger.error("获取 Bundle[{}] 的 [{}] 包下所有类名发生错误。", bundle.getSymbolicName(), pack);
382 			logger.error("", e);
383 		}
384 		return classes;
385 	}
386 
387 	/**
388 	 * 判断指定的 Java 类是否实体类,即当前类及其父类是否添加了 {@link javax.persistence.Entity} 注解
389 	 * @param c Java类
390 	 * @return 如果是实体类,则返回 true;反之则返回 false
391 	 */
392     private boolean isEntity(Class<?> c) {
393         // 如果是接口、枚举或注解,则直接返回 false
394         if(c.isInterface() || c.isEnum() || c.isAnnotation())
395             return false;
396 
397         Entity e = c.getAnnotation(Entity.class);
398         if (e != null)
399             return true;
400         
401         c = c.getSuperclass();
402         if (null == c || c.getName().equals(Object.class.getName()))
403             return false;
404 
405         return isEntity(c);
406     }
407 
408     public void setBaseDAO(IBaseDAO baseDAO) {
409         this.baseDAO = baseDAO;
410     }
411 
412     @Override
413 	public void stop(BundleContext arg0) throws Exception {
414 
415 	}
416 
417 }