package io.mycat.server.classloader; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.io.*; import java.util.Iterator; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; /** * used for mycat's catlet class loader ,catlet's class file is stored in * Mycat_home/catlet dir * * @author wuzhih * */ public class DynaClassLoader { private static final Logger LOGGER = LoggerFactory .getLogger("DynaClassLoader"); /** key- class full name */ private static Map<String, DynaClass> loadedDynaClassMap = new ConcurrentHashMap<String, DynaClass>(); private final String extClassHome; private final MyDynaClassLoader myClassLoader; private final long classCheckMilis; public DynaClassLoader(String extClassHome, int classCheckSeconds) { super(); this.extClassHome = extClassHome; classCheckMilis = classCheckSeconds * 1000L; myClassLoader = new MyDynaClassLoader(); LOGGER.info("dyna class load from " + extClassHome + ",and auto check for class file modified every " + classCheckSeconds + " seconds"); } public Object getInstanceofClass(String className) throws Exception { DynaClass dynaClass = loadedDynaClassMap.get(className); boolean needReload = (dynaClass == null || (dynaClass .needReloadClass(classCheckMilis) && checkChanged(dynaClass))); Class<?> newClass = null; if (needReload) { newClass = myClassLoader.loadClass(className); dynaClass = loadedDynaClassMap.get(className); } else { newClass = dynaClass.realClass; } if (dynaClass != null) { Object val = dynaClass.classObj; if (val == null) { val = dynaClass.realClass.newInstance(); dynaClass.classObj = val; } return val; } else { return newClass.newInstance(); } } /** * 加载某个类的字节码 * * @param c * @return * @throws IOException */ private static byte[] loadFile(String path) throws IOException { BufferedInputStream in = null; try { in = new BufferedInputStream(new FileInputStream(path)); byte[] readed = new byte[1024 * 4]; ByteArrayOutputStream out = new ByteArrayOutputStream(); int count = 0; while ((count = in.read(readed)) != -1) { out.write(readed, 0, count); } return out.toByteArray(); } finally { if (in != null) { in.close(); } } } private boolean checkChanged(DynaClass dynaClass) throws IOException { boolean isChanged = false; File f = new File(dynaClass.filePath); if (f.exists()) { long newTime = f.lastModified(); long oldTime = dynaClass.lastModified; if (oldTime != newTime) { // need reload dynaClass.lastModified = newTime; dynaClass.classObj = null; dynaClass.realClass = null; isChanged = true; } } return isChanged; } class MyDynaClassLoader extends ClassLoader { public MyDynaClassLoader() { } public MyDynaClassLoader(ClassLoader parentLoader) { super(parentLoader); } /** * 加载某个类 * * @param c * @return * @throws ClassNotFoundException * @throws IOException */ public Class<?> loadClass(String name) throws ClassNotFoundException { if (name.startsWith("java") || name.startsWith("sun") || name.startsWith("org.opencloudb")) { return super.loadClass(name); } DynaClass dynaClass = loadedDynaClassMap.get(name); if (dynaClass != null) { if (dynaClass.realClass != null) { return dynaClass.realClass; } } else { try { dynaClass = searchFile(extClassHome, name); } catch (Exception e) { LOGGER.error("SearchFileError", e); } } if (dynaClass == null) { return super.loadClass(name); } else { LOGGER.info("load class from file "+dynaClass.filePath); Class<?> cNew = null; if (dynaClass.isJar) { cNew =dynaClass.realClass; } else { byte[] content; try { content = loadFile(dynaClass.filePath); } catch (IOException e) { throw new ClassNotFoundException(e.toString()); } cNew = super.defineClass(name, content, 0,content.length); dynaClass.realClass = cNew; } dynaClass.classObj = null; loadedDynaClassMap.put(name, dynaClass); return cNew; } } private DynaClass searchFile(String classpath, String fileName) throws Exception { DynaClass dynCls = null; String path = fileName.replace('.', File.separatorChar) + ".class"; File f = new File(classpath, path); if (f.isFile()) { String theName = f.getPath(); LOGGER.info("found " + theName); dynCls = new DynaClass(f.getPath()); dynCls.lastModified = f.lastModified(); return dynCls; } else { path = fileName.replace('.', File.separatorChar) + ".jar"; //classpath="D:\\code\\mycat\\Mycat-Server\\catlet\\"; LOGGER.info("jar " + classpath + " file " + path); f = new File(classpath, path); if (f.isFile()) { try { dynCls = new DynaClass(f.getPath()); dynCls.lastModified = f.lastModified(); dynCls.realClass=JarLoader.loadJar(classpath+"/"+path,fileName); dynCls.isJar=true; return dynCls; } catch(Exception err) { return null; } } return null; } } } public void clearUnUsedClass() { long deadTime = System.currentTimeMillis() - 30 * 60 * 1000L; Iterator<Map.Entry<String, DynaClass>> itor = loadedDynaClassMap .entrySet().iterator(); while (itor.hasNext()) { Map.Entry<String, DynaClass> entry = itor.next(); DynaClass dyCls = entry.getValue(); if (dyCls.lastModified < deadTime) { LOGGER.info("clear unused catlet " + entry.getKey()); dyCls.clear(); itor.remove(); } } } } class DynaClass { public final String filePath; public volatile long lastModified; public Class<?> realClass; public Object classObj; public boolean isJar=false; public boolean needReloadClass(long classCheckMilis) { if (lastModified + classCheckMilis < System.currentTimeMillis()) { return true; } else { return false; } } public void clear() { this.realClass = null; this.classObj = null; } public DynaClass(String filePath) { super(); this.filePath = filePath; } }