Java tutorial
/** * Copyright 2012 Alex Jones * * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * * @author unclealex72 * */ package uk.co.unclealex.executable.generator.jar; import java.io.IOException; import java.net.URISyntaxException; import java.net.URL; import java.net.URLClassLoader; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; import java.util.Arrays; import java.util.Collections; import java.util.List; import java.util.jar.JarEntry; import java.util.jar.JarInputStream; import org.apache.commons.io.FileUtils; import org.junit.After; import org.junit.Assert; import org.junit.Before; import org.junit.Test; import org.objectweb.asm.ClassWriter; import org.objectweb.asm.MethodVisitor; import org.objectweb.asm.Opcodes; import com.google.common.collect.Iterables; import com.google.common.collect.Lists; import com.google.inject.AbstractModule; /** * @author alex * */ public class JarServiceImplTest { private Path tempDir; private String[] classNames = new String[] { "One", "jar.Two", "jar.Three", "jar.test.Four", "jar.test.Five" }; private Path jarFile; @Before public void setUp() throws IOException { tempDir = Files.createTempDirectory("jar-service-"); ClassLoader classLoader = getClass().getClassLoader(); // Check none of these classes exist already. for (String className : classNames) { try { classLoader.loadClass(className); Assert.fail("Class " + className + " already exists."); } catch (ClassNotFoundException e) { // This is good news. } } Path classDir = tempDir.resolve("classes"); for (String className : classNames) { String classFilename = className.replace('.', '/') + ".class"; Path classFile = classDir.resolve(classFilename); Files.createDirectories(classFile.getParent()); byte[] clazz = generateClass(className); Files.write(classFile, clazz); } JarService jarService = new JarServiceImpl(); jarFile = tempDir.resolve("jar").resolve("test.jar"); Files.createDirectories(jarFile.getParent()); jarService.generateJar(classDir, jarFile); Assert.assertTrue("The jar file was not created.", Files.isReadable(jarFile)); } @Test public void testJarCreation() throws Exception { List<String> actualEntryNames = Lists.newArrayList(); JarInputStream jarIn = new JarInputStream(Files.newInputStream(jarFile)); JarEntry jarEntry; while ((jarEntry = jarIn.getNextJarEntry()) != null) { actualEntryNames.add(jarEntry.getName()); } String[] expectedEntryNames = new String[] { "One.class", "jar/", "jar/Two.class", "jar/Three.class", "jar/test/", "jar/test/Four.class", "jar/test/Five.class" }; Arrays.sort(expectedEntryNames); Collections.sort(actualEntryNames); Assert.assertArrayEquals("The wrong entries were found in the jar.", expectedEntryNames, Iterables.toArray(actualEntryNames, String.class)); // Now test the classes can be loaded. ClassLoader testClassLoader = new URLClassLoader(new URL[] { jarFile.toUri().toURL() }, getClass().getClassLoader()); for (String className : classNames) { try { Class<?> clazz = testClassLoader.loadClass(className); String value = (String) clazz.getMethod("execute").invoke(clazz.newInstance()); Assert.assertEquals("Executing class " + className + " returned the wrong string.", className, value); } catch (ClassNotFoundException e) { Assert.fail("Could not load class " + className); } } } @Test public void testJarDefines() throws IOException { JarServiceImpl jarService = new JarServiceImpl(); for (String className : classNames) { Assert.assertTrue("The jar file was expected to contain the definition of " + className, jarService.jarDefinesClass(jarFile, className)); Assert.assertFalse("The jar file was not expected to contain the definition of " + className + "x", jarService.jarDefinesClass(jarFile, className + "x")); } } @After public void tearDown() throws IOException { FileUtils.deleteDirectory(tempDir.toFile()); } @Test public void testSearchClassloadersExpectingJar() throws IOException, URISyntaxException { Path actualGuicePath = new JarServiceImpl().findJarFileOrDirectoryDefiningClass(getClass().getClassLoader(), AbstractModule.class.getName()); Assert.assertNotNull("Could not find the guice jar file.", actualGuicePath); Assert.assertEquals("The wrong jar file was found to define guice's AbstractModule.", "guice-3.0.jar", actualGuicePath.getFileName().toString()); } @Test public void testSearchClassloadersExpectingDirectory() throws IOException, URISyntaxException { Class<? extends JarServiceImplTest> clazz = getClass(); Path actualPath = new JarServiceImpl().findJarFileOrDirectoryDefiningClass(clazz.getClassLoader(), clazz.getName()); Path expectedPath = Paths.get(clazz.getClassLoader().getResource("test-resources.root").toURI()) .getParent(); Assert.assertNotNull("Could not find the directory containing this class.", actualPath); Assert.assertEquals("The wrong directory was found to define this class.", expectedPath, actualPath); } protected byte[] generateClass(String className) { ClassWriter cw = new ClassWriter(0); MethodVisitor mv; cw.visit(Opcodes.V1_7, Opcodes.ACC_PUBLIC + Opcodes.ACC_SUPER, className.replace('.', '/'), null, "java/lang/Object", null); mv = cw.visitMethod(Opcodes.ACC_PUBLIC, "<init>", "()V", null, null); mv.visitCode(); mv.visitVarInsn(Opcodes.ALOAD, 0); mv.visitMethodInsn(Opcodes.INVOKESPECIAL, "java/lang/Object", "<init>", "()V"); mv.visitInsn(Opcodes.RETURN); mv.visitMaxs(1, 1); mv.visitEnd(); mv = cw.visitMethod(Opcodes.ACC_PUBLIC, "execute", "()Ljava/lang/String;", null, null); mv.visitCode(); mv.visitLdcInsn(className); mv.visitInsn(Opcodes.ARETURN); mv.visitMaxs(1, 1); mv.visitEnd(); cw.visitEnd(); return cw.toByteArray(); } }