uk.co.unclealex.executable.generator.jar.JarServiceImplTest.java Source code

Java tutorial

Introduction

Here is the source code for uk.co.unclealex.executable.generator.jar.JarServiceImplTest.java

Source

/**
 * 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();

    }
}