jar文件解析,加载,卸载的简单实现

来源:互联网 发布:nginx 1.12.0 配置 编辑:程序博客网 时间:2024/05/17 08:36

在项目开发时,由于需求的更变,需要实现对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();}@Overridepublic 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();}}@Overridepublic 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();//}//}@Overridepublic 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();}@Overridepublic 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();}@Overridepublic 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();}@Overridepublic 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();}@Overridepublic Object getLoader(String loaderName) {return this.loaderMap.get(loaderName);}@Overridepublic void init() {}@Overridepublic void destroy() {}@Overridepublic 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包下的所以类文件classJarEntry 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包中固定包下的类名).

原创粉丝点击