1. 程式人生 > >jar檔案解析,載入,解除安裝的簡單實現

jar檔案解析,載入,解除安裝的簡單實現

在專案開發時,由於需求的更變,需要實現對jar檔案的上傳,解析,載入,解除安裝等功能

1.MyURLClassLoader.java

public class MyURLClassLoader extends URLClassLoader {
	private List<JarURLConnection> cachedJarFiles = new ArrayList();
	
	public MyURLClassLoader() {
		super(new URL[] {}, findParentClassLoader());
	}
	
	/**
	* 定位基於當前上下文的父類載入器
	* @return 返回可用的父類載入器.
	*/
	private static ClassLoader findParentClassLoader() {
		ClassLoader parent = MyURLClassLoader.class.getClassLoader();
		if (parent == null) {
			parent = MyURLClassLoader.class.getClassLoader();
		}
		if (parent == null) {
			parent = ClassLoader.getSystemClassLoader();
		}
		return parent;
	}
	
	 /**
	* 將指定的檔案url新增到類載入器的classpath中去,並快取jar connection,方便以後解除安裝jar
	* @param 一個可想類載入器的classpath中新增的檔案url
	*/
	public void addURLFile(URL file) {
		try {
		// 開啟並快取檔案url連線
	
			URLConnection uc = file.openConnection();
			if (uc instanceof JarURLConnection) {
				uc.setUseCaches(true);
				((JarURLConnection) uc).getManifest();
				cachedJarFiles.add((JarURLConnection)uc);
			}
		} catch (Exception e) {
		System.err.println("Failed to cache plugin JAR file: " + file.toExternalForm());
		}
		addURL(file);
	}
	
	/**
	* 解除安裝jar包
	*/
	public void unloadJarFiles() {
		List<JarURLConnection> tempDel = new ArrayList<>();
		for (JarURLConnection url : cachedJarFiles) {
			try {
				tempDel.add(url);
				System.err.println("Unloading plugin JAR file " + url.getJarFile().getName());
				url.getJarFile().close();
				url=null;
			} catch (Exception e) {
				System.err.println("Failed to unload JAR file\n"+e);
			}
		}
		cachedJarFiles.removeAll(tempDel);
		tempDel = null;
	}
	
	public int getCachedJarCount() {
		return cachedJarFiles.size();
	}
}

2.CustomFunctionJarManager.java

public interface CustomFunctionJarManager {
	public void loadJar(String path,String loaderName);
	public void unloadJar(String loaderName);
//	public void tempParse(String path,JarParse jarParse);
	public byte[] createChecksum(String filename) throws Exception;
	public byte[] createChecksum(InputStream fis) throws Exception;
	public String getMD5Checksum(String filename) throws Exception;
	public String getMD5Checksum(InputStream fis) throws Exception;
	public Object getLoader(String loaderName);
	public void init();
	public void destroy();
	public boolean isloaded(String loaderName);
}

3.CustomFunctionJarManagerImpl.java

public class CustomFunctionJarManagerImpl implements CustomFunctionJarManager{
	private static Map loaderMap = new HashMap();
	
	public CustomFunctionJarManagerImpl(){
		super();
	}
	
	@Override
	public synchronized void loadJar(String path,String loaderName){
		try {
                        String name = loaderName==null?path:loaderName;
			unloadJar(name);
			MyURLClassLoader loader = new MyURLClassLoader();
			URL url = new File(path).toURI().toURL();
			String jarStr = "jar:" + url.toExternalForm() + "!/";
			loader.addURLFile( new URL(jarStr) );
                        this.loaderMap.put(name, loader);
		} catch (MalformedURLException e) {
			e.printStackTrace();
		}
	}
	
	@Override
	public synchronized void unloadJar(String loaderName){
		MyURLClassLoader loader = (MyURLClassLoader) this.getLoader(loaderName);
		if(null != loader) {
			loader.unloadJarFiles();
			this.loaderMap.remove(loaderName);
		}
	}
	
//	@Override
//	public synchronized void tempParse(String path,JarParse jarParse) {
//		try {
//			URL url = new File(path).toURI().toURL();
//			String jarStr = "jar:" + url.toExternalForm() + "!/";
//			loader.addURLFile( new URL(jarStr) );
//			
//			jarParse.parseJar(path, loader);
//		} catch (MalformedURLException e) {
//			e.printStackTrace();
//		} finally {
//			loader.unloadJarFiles();
//			System.gc();
//		}
//	}
	
	@Override
	public synchronized byte[] createChecksum(String filename) throws Exception{
		InputStream fis = new FileInputStream(filename);
		
		byte[] buffer = new byte[1024];
		int len;
		MessageDigest complete = MessageDigest.getInstance("MD5");

		while((len=fis.read(buffer)) > -1) {
			complete.update(buffer, 0, len);
		}
		fis.close();
		return complete.digest();
	}
	
	@Override
	public synchronized byte[] createChecksum(InputStream fis) throws Exception{
		
		byte[] buffer = new byte[1024];
		int len;
		MessageDigest complete = MessageDigest.getInstance("MD5");

		while((len=fis.read(buffer)) > -1) {
			complete.update(buffer, 0, len);
		}
		fis.close();
		return complete.digest();
	}
	
	@Override
	public synchronized String getMD5Checksum(String filename) throws Exception{
		byte[] b = createChecksum(filename);
		StringBuffer result = new StringBuffer("");
		
		for (int i = 0,len = b.length; i < len; i++) {
			result.append(Integer.toString((b[i] & 0xff) + 0x100).substring(1));
		}
		
		return result.toString();
	}
	
	@Override
	public synchronized String getMD5Checksum(InputStream fis) throws Exception{
		StringBuffer result = new StringBuffer("");
		byte[] b = createChecksum(fis);
		for (int i = 0,len = b.length; i < len; i++) {
			result.append(Integer.toString((b[i] & 0xff) + 0x100).substring(1));
		}
		
		return result.toString();
	}

	@Override
	public Object getLoader(String loaderName) {
		return this.loaderMap.get(loaderName);
	}

	@Override
	public void init() {
	}

	@Override
	public void destroy() {
	}

	@Override
	public boolean isloaded(String loaderName) {
		MyURLClassLoader loader = (MyURLClassLoader) getLoader(loaderName);
		return null!=loader;
	}
}

4.CustomFunctionJarManagerFactory.java

public class CustomFunctionJarManagerFactory {
	
	public static CustomFunctionJarManager manager = null;
	
	public static synchronized CustomFunctionJarManager getCustomFunctionJarManager(String className){
		if (null == manager) {
			if(null != className && !"".equals(className)){
				try {
					manager = (CustomFunctionJarManager) Class.forName(className).newInstance();
				} catch (Exception e) {
					ARE.getLog().error("CustomFunctionJarManager獲取失敗", e);
					manager = new CustomFunctionJarManagerImpl();
				}
			}
			else {
				manager = new CustomFunctionJarManagerImpl();
			}
		}
		try {
			manager.init();
		} catch (Exception e) {
			ARE.getLog().error("CustomFunctionJarManager初始化失敗", e);
		}
		return manager;
	}
	
	public static synchronized void release() {

		if (manager != null) {
			try {
				manager.destroy();
				manager = null;
			} catch (Exception e) {
				ARE.getLog().error("CustomFunctionJarManager銷燬失敗", e);
			}
		}
		return;
	}
}

5.使用情況

ServletContext application = config.getServletContext();
		
		Map<String,String> jarMap = new HashMap<String,String>();
		Map<String,String> classMap = new HashMap<String,String>();
		application.setAttribute("JarDetails", jarMap);             //儲存jar檔案MD5值,表示式名
		application.setAttribute("ClassMD5ValueMap", classMap);                            //儲存表示式class檔案對應MD5值
		Connection conn = null;
		
		try {
			BizObjectManager bm=JBOFactory.getBizObjectManager("xxxxxxxxxxxxxxxxxxxxxxxxx");
			
			String defaultPath = config.getServletContext().getRealPath("/");//專案工程根目錄
			String projectName = config.getServletContext().getContextPath();//專案工程名
			final String filePath = "CUSTOM_FUNCTION_UPDOAD_FILE_PATH";
			final String jarPath = "CUSTOM_FUNCTION_JAR_NAME";
			String rootParse = ARE.getProperty(filePath,defaultPath.substring(0, defaultPath.length() - projectName.length()) + "/cfClasses"); //jar包位置
			String jarName = ARE.getProperty(jarPath, "thread.jar");//jar包名
			
			CustomFunctionJarManager manager = CustomFunctionJarManagerFactory.getCustomFunctionJarManager(null);
			
			synchronized(manager) {//執行緒安全
				File file = new File(rootParse, jarName);
				if(!file.getParentFile().exists()) {
					file.getParentFile().mkdirs();
				}
				if(!file.exists()){
					jarMap.put("MD5Value", "");
					jarMap.put("FuncList", "");
					bm.createQuery("delete from O");
					return;
				}
				else {
					manager.loadJar(file.getAbsolutePath());
					jarMap.put("MD5Value", manager.getMD5Checksum(file.getAbsolutePath()));
					
					JarFile jarFile = null;
					jarFile = new JarFile(file.getAbsolutePath());
					Enumeration<JarEntry> entryList = jarFile.entries();
					StringBuffer sbf = new StringBuffer("");
					while(entryList.hasMoreElements()) {//在jar檔案中查詢xxx.xxx.xxx包下的所以類檔案class
						JarEntry jarEntry = entryList.nextElement();
						String tempName = jarEntry.getName();
						if(tempName.startsWith("xxx/xxx/xxx/") && tempName.endsWith(".class")) {
							Pattern p = Pattern.compile("[^0-9a-zA-Z/.]");//對掉內部類等情況
							Matcher m = p.matcher(tempName);
							if(!m.find()){
								classMap.put(tempName.substring(tempName.lastIndexOf("/") + 1, tempName.lastIndexOf(".")), manager.getMD5Checksum(jarFile.getInputStream(jarEntry)));
								sbf.append(tempName.substring(tempName.lastIndexOf("/") + 1, tempName.lastIndexOf(".")));
								sbf.append(",");
							}
						}						
					}
					if(!"".equals(sbf.toString())) {
						jarMap.put("FuncList", sbf.substring(0, sbf.length() - 1));
					}
					else {
						jarMap.put("FuncList", "");
						return;
					}
					
					List<BizObject> funcNames = bm.createQuery("select funcName from O").getResultList(false);
					StringBuffer funcNamesBuffer = new StringBuffer(",");
					for (BizObject bizObject : funcNames) {
						funcNamesBuffer.append(bizObject.getAttribute("funcName").getString());
						funcNamesBuffer.append(",");
					}
					String funcNamesStr = funcNamesBuffer.toString();
					String[] sbfArr = sbf.substring(0, sbf.length() - 1).split(",");				
					MyURLClassLoader loader = (MyURLClassLoader) manager.getLoader();
					
					Class.forName("com.mysql.jdbc.Driver");   
					conn = DriverManager.getConnection("jdbc:xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx", "craw", "craw");   
					conn.setAutoCommit(false); 
					
					String sql = "INSERT INTO xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx VALUES(?,?,?,?,?)";
					String sql2 = "DELETE FROM xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx WHERE funcName=?";
					PreparedStatement prest = conn.prepareStatement(sql);
					PreparedStatement prest2 = conn.prepareStatement(sql2);
					
					for (int i = 0; i < sbfArr.length; i++) {//jar檔案中的表示式類名與資料庫比較,jar中存在,資料庫中不存在的,批量新增
						if(!funcNamesStr.contains(","+sbfArr[i]+",")){
							boolean existFlag = false;
							Class cls = loader.loadClass("com.thread.lock."+sbfArr[i]);
							Method[] methods = cls.getMethods();
							String templateName = sbfArr[i] +"(";
							for (Method method : methods) {
								if("action".equals(method.getName())) {
									existFlag = true;
									int argsCount = method.getGenericParameterTypes().length;
									for (int n = 1; n < argsCount; n++) {
										templateName += "${" + n + "},";
									}
									if (argsCount > 1) {
										templateName = templateName.substring(0, templateName.length() - 1);
									}
									templateName += ")";
									
								 prest.setString(1, sbfArr[i]);;   
						    prest.setString(2, templateName);   
						    prest.setString(3, "1");   
						    prest.setString(4, new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()));   
						    prest.setString(5, new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()));   
						    prest.addBatch();
						    
								}
							}
							if(!existFlag) {
								classMap.remove(sbfArr[i]);
							}
							cls = null;
							templateName = "";
						}
						else {
							funcNamesStr.replace(","+sbfArr[i]+",", ",");
						}
					}					
					
					loader = null;
					
					prest.executeBatch();   
					
					if(funcNamesStr.length() > 1) {//jar中不存在,資料庫中存在的,批量刪除
						String[] noExistArr = funcNamesStr.substring(1, funcNamesStr.length() - 1).split(",");
						for (int i = 0; i < noExistArr.length; i++) {
							prest2.setString(1, noExistArr[i]);
							prest2.addBatch();
						}
						prest2.executeBatch();
					}
					
					conn.commit();   
					conn.close();   
					
					Set set = classMap.keySet();
					for (Object object : set) {
						System.out.print(object + "  :  ");
						System.out.println(classMap.get(object));
					}
				}
				
			}
			
		} catch (Exception e) {
			ARE.getLog().error("初始化儲存jarDetails資訊出錯!", e);
		} finally {
			if(null != conn) {
				try {
					conn.close();
				} catch (SQLException e) {
					ARE.getLog().error("資料庫連線關閉失敗", e);
				}
			}
		}

說明

MyURLClassLoader類主要是實現了jar檔案的載入和解除安裝(由於原有的URLClassLoader類對jar檔案的解除安裝功能不支援,所以需要早我實現一個MyURLClassLoader類).在使用時,通過一個管理類CustonFunctionJarManager來使用,而管理類通過工廠獲得,並保證全域性唯一,以控制執行緒安全.使用情況是在專案的啟動時,需要到指定位置讀取指定jar包,並解析jar包中在某一包下的所以類的類名,同時儲存對於的class檔案的MD5值在全域性.之後從資料庫中查出所以在資料庫中的類名.將在jar中存在的,資料庫中不存在的進行批量新增,將資料庫中存在的,jar中不存在的,進行批量刪除.之後在對jar包的上傳更新時,可以從全域性中取到jar包的MD5值,來判斷是否需要更新,而表示式class檔案的MD5值的作用是,在jar需要更新時,判斷資料庫中對應的表示式名是否需要更新(表示式名即為jar包中固定包下的類名).