简体   繁体   English

如何创建内存中的 JarFile?

[英]How to create an in-memory JarFile?

I'm trying to write a function like:我正在尝试编写 function ,例如:

public Map<String, Document> getTestXml(JarFile jarFile) {
    Map<String, Document> result = Maps.newHashMap();

    Enumeration<JarEntry> jarEntries = jarFile.getEntries();
    while (jarEntries.hasMoreElements()) {
        JarEntry jarEntry = jarEntries.nextElement();

        String name = jarEntry.getName();
        if (name.endsWith(".class") && !name.contains("$")) {
            String testClassName = name.replace(".class", "").replace("/", ".");
            String testXmlFilename = "TEST-" + testClassName + ".xml";

            InputStream testXmlInputStream = testJarFile.getInputStream(
                    testJarFile.getJarEntry(testXmlFilename));

            DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance();
            DocumentBuilder documentBuilder = documentBuilderFactory.newDocumentBuilder();
            Document testXmlDocument = documentBuilder.parse(testXmlInputStream);

            result.put(testClassName, testXmlDocument);
        }
    }

    return result;
}

And I would like to write a unit test that doesn't actually create a JarFile on the file system.而且我想编写一个实际上不会在文件系统上创建 JarFile 的单元测试。 I've tried to look for how to create a File object in memory, but haven't found anything like that.我试图寻找如何在 memory 中创建文件 object,但没有找到类似的东西。 Anyone have any suggestions?有人有什么建议吗?

Instead of a JarFile, use a JarInputStream.使用 JarInputStream 代替 JarFile。 For testing, hook the JarInputStream up to a ByteArrayInputStream loaded with in-memory jar data, and in normal operation hook it up to the input stream from a file.为了测试,将 JarInputStream 连接到加载了内存中 jar 数据的 ByteArrayInputStream,并在正常操作中将其连接到文件中的输入 stream。

File() objects all live within some file system name space. File() 对象都存在于某个文件系统名称空间中。 Which gives you two basic choices:这为您提供了两个基本选择:

1). 1)。 If you're using an O/S with a tempfs file system, create it there.如果您使用的是带有 tempfs 文件系统的操作系统,请在此处创建它。 2). 2)。 Use File.createTempFile() and set the delete-on-exit attribute.使用 File.createTempFile() 并设置 delete-on-exit 属性。

The usual approach of creating a sub-class ("public MemoryFile extends File"...) doesn't work because a File() object doesn't contain the methods for doing actual I/O, just for holding the name of the object and doing a few file system operations.创建子类的常用方法(“public MemoryFile extends File”...)不起作用,因为 File() object 不包含进行实际 I/O 的方法,只是为了保存object 并做一些文件系统操作。

You can use EasyMock to create a mock object of class JarFile.您可以使用EasyMock创建一个模拟 object 的 class JarFile。 For the mock object you specify which methods are called in the test and what the return values are without the need to actually create a JAR file on the file system.对于模拟 object,您可以指定在测试中调用哪些方法以及返回值是什么,而无需在文件系统上实际创建 JAR 文件。

Then call your getTestXml() method with your mock JarFile instance.然后使用您的模拟 JarFile 实例调用您的 getTestXml() 方法。

It needs some time to get used to it, but then you will see it's worth the effort.它需要一些时间来适应它,但是你会发现它是值得的。

Update The given source code doesn't compile, so here is a compilable version:更新给定的源代码无法编译,所以这是一个可编译的版本:

public class JarFileUser {
  public Map<String, Document> getTestXml(JarFile jarFile) throws IOException, ParserConfigurationException, SAXException {
    Map<String, Document> result = new HashMap<String, Document>();

    Enumeration<JarEntry> jarEntries = jarFile.entries();
    while (jarEntries.hasMoreElements()) {
      JarEntry jarEntry = jarEntries.nextElement();

      String name = jarEntry.getName();
      if (name.endsWith(".class") && !name.contains("$")) {
        String testClassName = name.replace(".class", "").replace("/", ".");
        String testXmlFilename = "TEST-" + testClassName + ".xml";

        InputStream testXmlInputStream = jarFile.getInputStream(jarFile.getJarEntry(testXmlFilename));

        DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance();
        DocumentBuilder documentBuilder = documentBuilderFactory.newDocumentBuilder();
        Document testXmlDocument = documentBuilder.parse(testXmlInputStream);

        result.put(testClassName, testXmlDocument);
      }
    }

    return result;
  }
}

Here is a test with EasyMock:这是一个使用 EasyMock 的测试:

public class JarFileUserTest {

  private JarFile mockJarFile;
  private Enumeration<JarEntry> mockJarEntries;

  private JarFileUser jarFileUser;
  private JarEntry first;
  private JarEntry second;
  private JarEntry firstXml;

  @Before
  public void setUp() throws Exception {
    jarFileUser = new JarFileUser();

    // Create a mock for the JarFile parameter
    mockJarFile = createMock(JarFile.class);

    // User Vector to provide an Enumeration of JarEntry-Instances 
    Vector<JarEntry> entries = new Vector<JarEntry>();
    first = createMock(JarEntry.class);
    second = createMock(JarEntry.class);

    entries.add(first);
    entries.add(second);

    expect(first.getName()).andReturn("mocktest.JarFileUser.class");
    expect(second.getName()).andReturn("mocktest.Ignore$Me.class");

    mockJarEntries = entries.elements();

    expect(mockJarFile.entries()).andReturn(mockJarEntries);

    // JarEntry for the XML file
    firstXml = createMock(JarEntry.class);

    expect(mockJarFile.getJarEntry("TEST-mocktest.JarFileUser.xml")).andReturn(firstXml);

    // XML contents
    ByteArrayInputStream is = new ByteArrayInputStream("<test>This is a test.</test>".getBytes("UTF-8"));
    expect(mockJarFile.getInputStream(firstXml)).andReturn(is);

    replay(mockJarFile);
    replay(first);
    replay(second);
    replay(firstXml);
  }

  @Test
  public void testGetTestXml() throws IOException, ParserConfigurationException, SAXException {
    Map<String, Document> map = jarFileUser.getTestXml(mockJarFile);
    verify(mockJarFile);
    verify(first);
    verify(second);
    verify(firstXml);

    assertEquals(1, map.size());
    Document doc = map.get("mocktest.JarFileUser");
    assertNotNull(doc);
    final Element root = (Element) doc.getDocumentElement();
    assertNotNull(root);
    assertEquals("test", root.getNodeName());
    assertEquals("This is a test.", root.getTextContent());
  }

}

Note on additional libraries JarFile is a class and not an interface so according to the EasyMock installation docs you should have Objenesis and cglib in your classpath.注意其他库JarFile 是 class 而不是接口,因此根据EasyMock 安装文档,您的类路径中应该有Objenesiscglib

You need to look at ByteArrayOutputStream and ByteArrayInputStream .您需要查看ByteArrayOutputStreamByteArrayInputStream Those are the in memory stream objects in Java.这些是memory stream 对象中的 Java 中的对象。 Use those and nothing will get written to disk.使用这些,什么都不会写入磁盘。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM