在内存jar /类文件中执行

时间:2010-07-17 08:54:23

标签: java memory jar

我在某个位置有一个jar文件(例如app.jar),可以作为来自http服务器的文件/流。问题是app.jar本身是一个经常更新的jar文件,在服务器上定期更新。 我有另一个jar文件(比如load.jar),任何用户都可以下载。 在运行时,用户运行load.jar,这需要在单独的进程中加载​​app.jar,将app.jar保存在内存中(不缓存到磁盘),然后执行app.jar并终止自身(加载。罐)。 有可能吗?任何帮助都非常感谢。 提前致谢。 问候, KT

---------------------------更新。

您好,

感谢大家的回复。但是,我想我还没有给出完整的图片。 附加了描述场景的图像 link text 。 4个罐子(实际的通用执行器)托管在中央服务器上。这些经常更新(最初每天3-4次,最终每天一次)。 托管其中一个成员的本地服务器,该服务器已初始化。 本地服务器上的启动程序会下载所有4个二进制文件并将其保留在内存中 - 不会缓存到磁盘。这些jar文件是自给自足的,并且不依赖于“本地服务器”上的任何库 - 它们实际上是从“本地服务器”调用jar。 “客户端”jar最终由“本地服务器”托管,并通过“本地服务器”通过webapp按需转发给其客户端。 此外,Launcher需要通过在单独的JVM中从服务器调用下载的Main jar来退出。

此致 KT

4 个答案:

答案 0 :(得分:3)

行。在我将szegedi文章的链接放入我之前的答案(诚实)之前,我制作了一个可以处理URL的jar启动器原型。就像作者所说,这并不难,可能还不完整。附在下面。我认为这是你需要的1/3。您的用户说(例如):

java -jar load.jar http://localhost:8080/app.jar

load.jar有两个角色:(1)调用JarLauncher(下面),因为它的主类(2)在localhost端口上提供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});

    }

}

答案 1 :(得分:2)

我建议您查看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");

答案 2 :(得分:2)

进一步编辑:

我扫描了网页并发现了这篇可能与您的问题有关的优秀文章:http://www.szegedi.org/articles/remotejars.html

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

编辑:

好的,我想我能更好地了解您的要求。虽然要求很棘手,所以让我告诉你我认为你想做什么,然后我会看到建议的解决方案。我可能会错的,然后我们再做一次。

要求:

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

解决方案?

您可能正在寻找的是一个Web服务器,它可以托管一个webapp,将更新的应用程序jar读入内存,然后将它们提供给webstart启动器。您将需要使用诸如Jetty / Tomcat之类的Web服务器,并编写一个简单的webapp以在其下运行。 webapp将轮询中央服务器以获取应用程序更新,并将通过它所服务的URL使应用程序jar可用于webstart。由于防火墙和可扩展性的原因,轮询中央服务器可能比让中央服务器将新应用程序推送到本地服务器更有意义。

我不知道有任何现成的webapps这样做,可能有一个。

但就像我说的那样,我可能仍然没有得到这个。要求:得爱他们。


根据你的问题,我将在这个答案中作出一些假设。听起来你想在本地机器上“load.jar”的生命周期中从远程机器缓存“app.jar”,以便将“load.jar”与“app.jar”的更改隔离开来“在其一生中。这似乎是一个好主意:我希望如果你使用URLClassLoader将“app.jar”带入“load.jar”空间,那么执行中途的更新可能会造成严重破坏。

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

鉴于这些目标,您需要找到或实现一个类加载器,当“load.jar”首次访问其中的类时,该类加载器会生成“app.jar”的快照。这并不难:我必须在我的One-JAR JarClassLoader中做类似的事情,它从Jar文件中加载Jar文件。在启动期间,它将所有找到的字节加载到内存中,然后使用该内存存储按需解析类。虽然可能效率低于延迟加载,但速度快且稳定,可以解决您的问题。

不幸的是,One-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

第678行。不要被这个类的大小吓倒,它是一堆特殊情况要处理,特殊情况。

如果我必须从头开始创建东西,我会继承URLClassLoader,覆盖loadClass,getResource和findResource方法(参见例如第281行,其中One-JAR为其自己的类加载器反转需要执行此操作),放入字节码缓存通过将整个“app.jar”加载到内存中作为由类/资源名称键索引的字节数组hashmap(再次是One-JAR),然后将其缓存在内存中。

我希望这会有所帮助。

- 西蒙

答案 3 :(得分:0)

你看过URLClassloader课了吗?

这里的JarClassloader可能很有用:http://www.java.happycodings.com/Other/code27.html