基于JFinal的web插件平台(完全分离)如何加载JFinal库?

行水流云 发布于 2015/03/02 19:09
阅读 652
收藏 0

@JFinal 你好,想跟你请教个问题:

最近正在做一个基于插件体系的WEB开发平台,而JFinal 本身自带插件功能本身不能焊好的实现我的构想。

其它的库因为不存在父ClassLoader调用子ClassLoader的情况,所以不需要深入,而JFinal 则存在这个问题。

JFinal 通过加载用户实现的JFinalConfig虚拟类来加载各种配置,这就导致JFinal.jar与用户的类必须在同一个ClassLoader下。有没有办法使它们分开,使JFinal在父ClassLoader下,而用户配置在子ClassLoader下。
这样实现一个WEBAPP插件平台共用JFinal。

目录结构:

下面是测试示例代码:

public class WebJettyStart {
	
	private String HOME_PATH = System.getProperty("user.dir");
	private String WEBROOT_PATH = "/";
	
	private int PORT = 80;
	
	private String LIB_PATH = HOME_PATH + File.separator + "lib";

	private String PLUGSINS_PATH = HOME_PATH + File.separator + "plugins";

	private String ROOT_PATH = HOME_PATH + File.separator + "root";

	private String CONF_PATH = HOME_PATH + File.separator + "conf";
	
	private ContextHandlerCollection contexts=new ContextHandlerCollection();
	
	public HotJetty() {
		// TODO 自动生成的构造函数存根
	}

	
	
	
	public static void main(String[] args) throws Exception {
		// TODO 自动生成的方法存根
		
		WebJettyStart start=new WebJettyStart();
		
		
		
		final ClassLoader parent = start.findParentClassLoader();

		String libDirString = System.getProperty("jStar.lib.dir");

		File libDir;
		if (libDirString != null) {
			// If the lib directory property has been specified and it
			// actually
			// exists use it, else use the default
			libDir = new File(libDirString);
			if (!libDir.exists()) {
				// Log.warn("Lib directory " + libDirString +
				// " does not exist. Using default " + DEFAULT_LIB_DIR);
				libDir = new File(start.LIB_PATH);
			}
		} else {
			libDir = new File(start.LIB_PATH);
		}

		// Unpack any pack files.
		start.unpackArchives(libDir, true);
		
		//解压插件Jar包,如果路径存在,不解,不存在则解压
		//TODO  解压代码... ...

		JStarClassLoader loader = new JStarClassLoader(parent, libDir);
		
		//这里载入插件lib===如果JFinal不调用子ClassLoader中的类,就不需要了
		loader.loadDir(new File(start.HOME_PATH+File.separator+"jfinal"+File.separator+"web"+File.separator+"WEB-INF"+File.separator+"lib"));
		
		
		Thread.currentThread().setContextClassLoader(loader);
		
		Server server = new Server();  
        Connector connector = new SelectChannelConnector();  
        connector.setPort(start.PORT); // 设置要监听的端口.  
        server.addConnector(connector);  
        
        
       
        ContextHandler context;//  = new WebAppContext();  
        
        
        //第一个项目 这个插件就是原JFinal自带的demo项目
        context = new WebAppContext(start.contexts, start.HOME_PATH+File.separator+"jfinal"+File.separator+"web", start.WEBROOT_PATH+"jfinal");
        
        //第二个项目 这个是我自建的项目,没有用JFinal,包含一个jsp,一个Servlet,Servlet打包在admin.jar中了,测试时,放在父ClassLoader或者子ClassLoader中都可以
        context = new WebAppContext(start.contexts, start.HOME_PATH+File.separator+"demo"+File.separator+"web", start.WEBROOT_PATH+"demo");
        
        server.setHandler(start.contexts);  
        server.start();  
	}

	

	/**
	 * Locates the best class loader based on context (see class description).
	 *
	 * @return The best parent classloader to use
	 */
	private ClassLoader findParentClassLoader() {
		ClassLoader parent = Thread.currentThread().getContextClassLoader();
		if (parent == null) {
			parent = this.getClass().getClassLoader();
			if (parent == null) {
				parent = ClassLoader.getSystemClassLoader();
			}
		}
		return parent;
	}

	/**
	 * Converts any pack files in a directory into standard JAR files. Each pack
	 * file will be deleted after being converted to a JAR. If no pack files are
	 * found, this method does nothing.
	 * @param libDir
	 *            the directory containing pack files.
	 * @param printStatus
	 *            true if status ellipses should be printed when unpacking.
	 */
	private void unpackArchives(File libDir, boolean printStatus) {
		// Get a list of all packed files in the lib directory.
		File[] packedFiles = libDir.listFiles(new FilenameFilter() {
			public boolean accept(File dir, String name) {
				return name.endsWith(".pack");
			}
		});

		if (packedFiles == null) {
			// Do nothing since no .pack files were found
			return;
		}

		// Unpack each.
		boolean unpacked = false;
		for (File packedFile : packedFiles) {
			try {
				String jarName = packedFile.getName().substring(0,
						packedFile.getName().length() - ".pack".length());
				// Delete JAR file with same name if it exists (could be due to
				// upgrade
				// from old Openfire release).
				File jarFile = new File(libDir, jarName);
				if (jarFile.exists()) {
					jarFile.delete();
				}

				InputStream in = new BufferedInputStream(new FileInputStream(
						packedFile));
				JarOutputStream out = new JarOutputStream(
						new BufferedOutputStream(new FileOutputStream(new File(
								libDir, jarName))));
				Pack200.Unpacker unpacker = Pack200.newUnpacker();
				// Print something so the user knows something is happening.
				if (printStatus) {
					System.out.print(".");
				}
				// Call the unpacker
				unpacker.unpack(in, out);

				in.close();
				out.close();
				packedFile.delete();
				unpacked = true;
			} catch (Exception e) {
				e.printStackTrace();
			}
		}
		// Print newline if unpacking happened.
		if (unpacked && printStatus) {
			System.out.println();
		}
	}
}

/**
 * A simple classloader to extend the classpath to
 * include all jars in a lib directory.<p>
 *
 * The new classpath includes all <tt>*.jar</tt> and <tt>*.zip</tt>
 * archives (zip is commonly used in packaging JDBC drivers). The extended
 * classpath is used for both the initial server startup, as well as loading
 * plug-in support jars.
 * * @author Derek DeMoro
 * @author Iain Shigeoka
 */
class JStarClassLoader extends URLClassLoader {

    /**
     * Constructs the classloader.
     *
     * @param parent the parent class loader (or null for none).
     * @param libDir the directory to load jar files from.
     * @throws java.net.MalformedURLException if the libDir path is not valid.
     */
    JStarClassLoader(ClassLoader parent, File libDir) throws MalformedURLException {
        super(new URL[] { libDir.toURI().toURL() }, parent);
        
        loadDir(libDir);

       
    }
    
    protected void loadDir( File libDir) throws MalformedURLException{
    	 File[] jars = libDir.listFiles(new FilenameFilter() {
             public boolean accept(File dir, String name) {
                 boolean accept = false;
                 String smallName = name.toLowerCase();
                 if (smallName.endsWith(".jar")) {
                     accept = true;
                 }
                 else if (smallName.endsWith(".zip")) {
                     accept = true;
                 }
                 return accept;
             }
         });

         // Do nothing if no jar or zip files were found
         if (jars == null) {
             return;
         }
         System.out.println("Loding ("+libDir+") Library ...");
         for (int i = 0; i < jars.length; i++) {
             if (jars[i].isFile()) {
                 addURL(jars[i].toURI().toURL());
                 System.out.println("  "+jars[i].getName());
             }
         }
    }
    
}




加载中
0
JFinal
JFinal
直接跟具体点,想实现什么样的功能,最好保持在 30 字以内说清楚.
0
行水流云
行水流云

一个WebApp,启动类加载Jfinal,然后顺次加载插件包,插件包就是一个普通War包,并包含插件的一些信息。然后启动Jetty,

public static void main(String[] args) throws Exception {
        // TODO 自动生成的方法存根
         
        WebJettyStart start=new WebJettyStart();
        final ClassLoader parent = start.findParentClassLoader();
        String libDirString = System.getProperty("jStar.lib.dir");
         //省略Jar查找
        JStarClassLoader loader = new JStarClassLoader(parent, libDir);
        //这里载入插件lib===因为JFinal调用子ClassLoader中的类,所以得加上才能正常访问插件中的类
        loader.loadDir(new File("插件路径1"+File.separator+"web"+File.separator+"WEB-INF"+File.separator+"lib"));
         
        Thread.currentThread().setContextClassLoader(loader);
         
        Server server = new Server();  
        Connector connector = new SelectChannelConnector();  
        connector.setPort(start.PORT); // 设置要监听的端口.  
        server.addConnector(connector);  
        ContextHandler context;//  = new WebAppContext();  
        context = new WebAppContext("插件路径1"+File.separator+"web", start.WEBROOT_PATH+"plugin1");
        context = new WebAppContext("插件路径2"+File.separator+"web", start.WEBROOT_PATH+"plugin2");
        server.setHandler(start.contexts);  
        server.start();  
    }



0
JFinal
JFinal

引用来自“行水流云”的评论

一个WebApp,启动类加载Jfinal,然后顺次加载插件包,插件包就是一个普通War包,并包含插件的一些信息。然后启动Jetty,

public static void main(String[] args) throws Exception {
        // TODO 自动生成的方法存根
         
        WebJettyStart start=new WebJettyStart();
        final ClassLoader parent = start.findParentClassLoader();
        String libDirString = System.getProperty("jStar.lib.dir");
         //省略Jar查找
        JStarClassLoader loader = new JStarClassLoader(parent, libDir);
        //这里载入插件lib===因为JFinal调用子ClassLoader中的类,所以得加上才能正常访问插件中的类
        loader.loadDir(new File("插件路径1"+File.separator+"web"+File.separator+"WEB-INF"+File.separator+"lib"));
         
        Thread.currentThread().setContextClassLoader(loader);
         
        Server server = new Server();  
        Connector connector = new SelectChannelConnector();  
        connector.setPort(start.PORT); // 设置要监听的端口.  
        server.addConnector(connector);  
        ContextHandler context;//  = new WebAppContext();  
        context = new WebAppContext("插件路径1"+File.separator+"web", start.WEBROOT_PATH+"plugin1");
        context = new WebAppContext("插件路径2"+File.separator+"web", start.WEBROOT_PATH+"plugin2");
        server.setHandler(start.contexts);  
        server.start();  
    }



看上去像是利用 Jetty 来实现动态加载与卸载 WebContext,找下 jetty context 相关文档可以解决。重点关注  org.eclipse.jetty.webapp.WebAppContext.java 这个类文件使用的文档
0
行水流云
行水流云
动态加载的插件WebContext之间,我不需要类相互能够直接调用。但是应该可以调用平台的公共类。即JFinal作为公共包放在公共库下,各个插件(其它WebContext)都可以使用
返回顶部
顶部