代码之家  ›  专栏  ›  技术社区  ›  Kapil

内存jar/类文件执行

  •  4
  • Kapil  · 技术社区  · 15 年前

    我在某个位置有一个jar文件(比如app.jar),可以作为http服务器的文件/流使用。问题是app.jar本身就是一个频繁更新的jar文件,在服务器上定期更新。 我有另一个jar文件(比如load.jar),任何用户都可以下载。 在运行时,用户运行load.jar,它需要在一个单独的进程中加载app.jar,将app.jar保存在内存中(不缓存到磁盘),然后执行app.jar并终止自身(load.jar)。 当做, 千吨

    谢谢大家的回复。然而,我想我还没有给出一个完整的画面。 形象 link text 是描述场景的附件。 4个jar(实际的公共执行器)托管在中央服务器上。这些都是经常更新的(可能一天3-4次,最初下降到一天一次)。 本地服务器上的启动程序下载所有4个二进制文件并将其保存在内存中—不缓存到磁盘。这些jar文件是自给自足的,不依赖于“本地服务器”上的任何库—它们实际上从“本地服务器”调用jar。 另外,启动程序需要通过在单独的JVM中从服务器调用下载的主jar来退出。

    当做,

    4 回复  |  直到 15 年前
        1
  •  3
  •   simontuffs    15 年前

    好 啊。在我把到szegedi文章的链接放进我之前的答案(老实说)之前,我原型化了一个可以处理url的jar启动器。正如作者所说,这并不难,可能并不完整。附件如下。我想这是你需要的三分之一。您的用户说(类似于):

    http://localhost:8080/app.jar

    load.jar有两个角色:(1)调用JarLauncher(如下)作为其主类(2)在本地主机端口上服务app.jar(在调用JarLauncher之前)。因此,load.jar读取其参数以确定要运行哪个应用程序。

    最后(最难的部分):您必须使URLClassLoader不命中临时磁盘。正如塞格迪的文章所说,这并不容易。或者,您可以编写自己的不需要磁盘缓存的网络感知类加载器,就像我的第一个建议,将URL作为URL连接的字节流加载到内存中,将其解码为JarInputStream,并从内存流中满足loadClass/findResource调用。

    抱歉,许可证文本的大小,但它可以让你做你喜欢的代码,只要你不责怪我(BSD)。

    --西蒙。


    /*
     * One-JAR(TM) (http://www.simontuffs.com/one-jar).  Copyright (c) 2004-2010, 
     * P. Simon Tuffs (simon@simontuffs.com).   All rights reserved.
     *
     * Redistribution and use in source and binary forms, with or without
     * modification, are permitted provided that the following conditions are met:
     *
     * Redistributions of source code must retain the above copyright notice, this
     * list of conditions and the following disclaimer.
     *
     * Redistributions in binary form must reproduce the above copyright notice,
     * this list of conditions and the following disclaimer in the documentation
     * and/or other materials provided with the distribution.  
     *
     * Neither the name of P. Simon Tuffs, nor the names of any contributors, 
     * nor the name One-JAR may be used to endorse or promote products derived 
     * from this software without specific prior written permission.
     *
     * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
     * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
     * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
     * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
     * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
     * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
     * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
     * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
     * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
     * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
     * POSSIBILITY OF SUCH DAMAGE.
     *
     * Including this file inside the built One-JAR file conforms with these terms.
     */
    
    import java.lang.reflect.Method;
    import java.net.MalformedURLException;
    import java.net.URL;
    import java.net.URLClassLoader;
    import java.util.ArrayList;
    import java.util.Arrays;
    import java.util.List;
    import java.util.jar.JarInputStream;
    
    /**
     * Programmatic equivalent of what happens when you say "java -jar <jar-file.jar>".
     * A better solution to debugging/running Jar files inside an IDE.
     * @author simon
     *
     */
    public class JarLauncher {
    
        public static URL getURL(String string) throws MalformedURLException {
            try {
                return new URL(string);
            } catch (MalformedURLException x) {
                return new URL("file:" + string);
            }
        }
    
        public static void main(String args[]) throws Exception {
            if (args.length < 1) {
                System.out.println("Usage: java [-Dkey=value...] JarLauncher <jar-file.jar>");
                System.exit(1);
            }
            String jar = args[0];
            args = Arrays.copyOfRange(args, 1, args.length);
            List<URL> urls = new ArrayList<URL>();
            // Main jar on the URL path.
            // Dig out the main class.
            urls.add(getURL(jar));
            URL jarurl = urls.get(0);
            JarInputStream jis = new JarInputStream(jarurl.openStream());
            String main = jis.getManifest().getMainAttributes().getValue("Main-Class");
            // OK to split on space, because embedded space is %20
            String classpaths[] = jis.getManifest().getMainAttributes().getValue("Class-Path").split(" ");
            for (String classpath: classpaths) {
                urls.add(getURL(classpath));
            }
            URLClassLoader loader = new URLClassLoader(urls.toArray(new URL[0]));
            Class<?> cls = loader.loadClass(main);
            Thread.currentThread().setContextClassLoader(loader);
            Method m = cls.getMethod("main", new Class[]{new String[0].getClass()});
            m.invoke(null, new Object[]{args});
    
        }
    
    }
    
    
        2
  •  2
  •   Manish Singh    15 年前

    我建议你看看 http://jcloader.sourceforge.net/

      JarClassLoader jcl = new JarClassLoader();
    
      //Loading classes from different sources
      jcl.add("myjar.jar");
      jcl.add(new URL("http://myserver.com/myjar.jar"));
      jcl.add(new FileInputStream("myotherjar.jar"));
      jcl.add("myclassfolder/");
    
      //Recursively load all jar files in the folder/sub-folder(s)
      jcl.add("myjarlib/");
    
      JclObjectFactory factory = JclObjectFactory.getInstance();
    
      //Create object of loaded class
      Object obj = factory.create(jcl, "mypack.MyClass");
    
        3
  •  2
  •   simontuffs    15 年前

    进一步编辑:

    我浏览了一下网页,发现了这篇优秀的文章,这篇文章可能也与你的问题有关: http://www.szegedi.org/articles/remotejars.html

    在这种情况下,我下面写的大部分内容都是无关紧要的,但我还是留着以防万一。

    编辑:

    要求:

    为WebStart应用程序提供服务的分布式缓存机制。应用程序(.jar)定期更改。它们是独立的。您希望有一个本地缓存能够为从中央服务器获得的Webstart jar文件提供服务,并将它们保存在内存中。我不明白你对退场的要求。

    解决?

    您可能正在寻找的是一个web服务器,它可以承载一个webapp,将更新的应用程序jar读入内存,然后将它们提供给webstart启动器。您需要使用Jetty/Tomcat等web服务器,并编写一个简单的webapp在其下运行。webapp将轮询中央服务器以获取应用程序更新,并通过其提供的url使webstart可以使用应用程序jar。出于防火墙和可伸缩性的考虑,轮询中央服务器可能比让中央服务器将新应用程序推送到本地服务器更有意义。

    我不知道任何现成的网络应用程序可以做到这一点,可能有一个。


    基于你的问题,我将在这个答案中做一些假设。听起来您想在本地机器上的“load.jar”的生存期内缓存远程机器上的“app.jar”,以便在其生存期内将“load.jar”从对“app.jar”的更改中隔离出来。这似乎是个好主意:我认为如果使用URLClassLoader将“app.jar”放入“load.jar”空间,那么在执行过程中进行更新可能会造成严重破坏。

    听起来你也不想让“load.jar”制作一个基于磁盘的“app.jar”副本——我认为这是一个合理的目标:谁想让旧的jar文件分散在机器上?谁想要与尝试写入临时文件相关的权限问题?

    考虑到这些目标,您需要找到或实现一个classloader,当“load.jar”第一次命中其中的类时,它会生成“app.jar”的快照。这并不难:我不得不在我的单JAR JarClassLoader中做类似的事情,它从JAR文件内部加载JAR文件。在启动期间,它将所有找到的字节加载到内存中,然后使用该内存存储按需解析类。虽然可能比惰性加载效率低,但它快速、稳定,可以解决您的问题。

    不幸的是,一个JAR不允许以这种方式缓存网络(或文件)资源。它通过一个普通的URL类加载器使用委托,这将使它不会缓存您的远程资源。

    我建议您查看一下CVS存储库: http://one-jar.cvs.sourceforge.net/viewvc/one-jar/one-jar/src/com/simontuffs/onejar/JarClassLoader.java?revision=1.58&view=markup

    如果我必须从头开始创建一些东西,我会将URLClassLoader子类化,重写loadClass、getResource和findResource方法(参见示例第281行,其中一个JAR根据自己的类装入器反转需要执行此操作),通过将整个“app.jar”作为字节数组hashmap加载到内存中,并通过类/资源名称键(这又是一个jar)索引,放入字节码缓存,然后您将被缓存在内存中。

    我希望这有帮助。

    --西蒙。

        4
  •  0
  •   Thorbjørn Ravn Andersen    15 年前