Package org.apache.felix.ipojo.manipulation

Source Code of org.apache.felix.ipojo.manipulation.InnerClassAdapterTest

/*
* 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.
*/

package org.apache.felix.ipojo.manipulation;

import junit.framework.Assert;
import org.apache.felix.ipojo.InstanceManager;
import org.apache.felix.ipojo.Pojo;
import org.apache.felix.ipojo.metadata.Element;
import org.junit.Ignore;
import org.junit.Test;
import org.mockito.Mockito;

import java.io.File;
import java.io.FilenameFilter;
import java.io.IOException;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.concurrent.Callable;

import static junit.framework.Assert.assertEquals;
import static org.fest.assertions.Assertions.assertThat;

/**
* Checks the inner class manipulation
*/
public class InnerClassAdapterTest {

    public static String baseClassDirectory = "target/test-classes/";

    public static ManipulatedClassLoader manipulate(String className, Manipulator manipulator) throws IOException {
        final File mainClassFile = new File(baseClassDirectory + className.replace(".", "/") + ".class");
        byte[] bytecode = ManipulatorTest.getBytesFromFile(
                mainClassFile);

        // Preparation.
        try {
            manipulator.prepare(bytecode);
        } catch (IOException e) {
            Assert.fail("Cannot read " + className);
        }

        // Inner class preparation
        for (String inner : manipulator.getInnerClasses()) {
            // Get the bytecode and start manipulation
            String resourcePath = inner + ".class";
            byte[] innerClassBytecode;
            try {
                innerClassBytecode = ManipulatorTest.getBytesFromFile(new File(baseClassDirectory + resourcePath));
                manipulator.prepareInnerClass(inner, innerClassBytecode);
            } catch (IOException e) {
                Assert.fail("Cannot find or analyze inner class '" + resourcePath + "'");
            }
        }

        // Now manipulate the classes.
        byte[] out = new byte[0];
        try {
            out = manipulator.manipulate(bytecode);
        } catch (IOException e) {
            Assert.fail("Cannot manipulate the class " + className + " : " + e.getMessage());
        }

        ManipulatedClassLoader classloader = new ManipulatedClassLoader(className, out);

        // Visit inner classes
        for (String inner : manipulator.getInnerClasses()) {
            // Get the bytecode and start manipulation
            String resourcePath = inner + ".class";
            byte[] innerClassBytecode;
            try {
                innerClassBytecode = ManipulatorTest.getBytesFromFile(new File(baseClassDirectory + resourcePath));
                byte[] manipulated = manipulator.manipulateInnerClass(inner, innerClassBytecode);
                classloader.addInnerClass(inner.replace("/", "."), manipulated);
            } catch (IOException e) {
                Assert.fail("Cannot find inner class '" + resourcePath + "'");
            }
        }

        // Lookup for all the other inner classes (not manipulated)
        File[] files = mainClassFile.getParentFile().listFiles(new FilenameFilter() {
            public boolean accept(File dir, String name) {
                return name.startsWith(mainClassFile.getName().substring(0, mainClassFile.getName().length() -
                        ".class".length()) + "$");
            }
        });

        for (File f : files) {
            String name = className + f.getName().substring(f.getName().indexOf("$"));
            name = name.substring(0, name.length() - ".class".length());
            byte[] innerClassBytecode = ManipulatorTest.getBytesFromFile(f);
            classloader.addInnerClassIfNotAlreadyDefined(name, innerClassBytecode);
        }

        return classloader;
    }

    public static ManipulatedClassLoader manipulate(String className, Manipulator manipulator,
                                                    ManipulatedClassLoader initial) throws IOException {
        byte[] bytecode = initial.get(className);
        final File mainClassFile = new File(baseClassDirectory + className.replace(".", "/") + ".class");
        String mainClass = className;

        // Preparation.
        try {
            manipulator.prepare(bytecode);
        } catch (IOException e) {
            Assert.fail("Cannot read " + className);
        }

        // Inner class preparation
        for (String inner : manipulator.getInnerClasses()) {
            // Get the bytecode and start manipulation
            String resourcePath = inner + ".class";
            byte[] innerClassBytecode;
            try {
                innerClassBytecode = initial.get(inner.replace("/", "."));
                manipulator.prepareInnerClass(inner, innerClassBytecode);
            } catch (IOException e) {
                Assert.fail("Cannot find or analyze inner class '" + resourcePath + "'");
            }
        }

        // Now manipulate the classes.
        byte[] out = new byte[0];
        try {
            out = manipulator.manipulate(bytecode);
        } catch (IOException e) {
            Assert.fail("Cannot manipulate the class " + className + " : " + e.getMessage());
        }

        ManipulatedClassLoader classloader = new ManipulatedClassLoader(className, out);

        // Visit inner classes
        for (String inner : manipulator.getInnerClasses()) {
            // Get the bytecode and start manipulation
            String resourcePath = inner + ".class";
            byte[] innerClassBytecode;
            try {
                innerClassBytecode = initial.get(inner.replace("/", "."));
                byte[] manipulated = manipulator.manipulateInnerClass(inner, innerClassBytecode);
                classloader.addInnerClass(inner.replace("/", "."), manipulated);
            } catch (IOException e) {
                Assert.fail("Cannot find inner class '" + resourcePath + "'");
            }
        }

        // Lookup for all the other inner classes (not manipulated)
        File[] files = mainClassFile.getParentFile().listFiles(new FilenameFilter() {
            public boolean accept(File dir, String name) {
                return name.startsWith(mainClassFile.getName().substring(0, mainClassFile.getName().length() -
                        ".class".length()) + "$");
            }
        });


        for (File f : files) {
            String name = className + f.getName().substring(f.getName().indexOf("$"));
            name = name.substring(0, name.length() - ".class".length());
            byte[] innerClassBytecode = ManipulatorTest.getBytesFromFile(f);
            classloader.addInnerClassIfNotAlreadyDefined(name, innerClassBytecode);
        }

        return classloader;
    }

    private static Element getInnerClassMetadataByName(Element[] inners, String name) {
        for (Element element : inners) {
            if (name.equals(element.getAttribute("name"))) {
                return element;
            }
        }
        return null;
    }

    private static Element getMethodByName(Element[] methods, String name) {
        for (Element element : methods) {
            if (name.equals(element.getAttribute("name"))) {
                return element;
            }
        }
        return null;
    }

    @Test
    public void testManipulatingTheInner() throws Exception {
        Manipulator manipulator = new Manipulator(this.getClass().getClassLoader());
        String className = "test.PojoWithInner";
        byte[] origin = ManipulatorTest.getBytesFromFile(new File(baseClassDirectory + className.replace(".",
                "/") + ".class"));

        ManipulatedClassLoader classloader = manipulate(className, manipulator);

        Class cl = classloader.findClass(className);
        Assert.assertNotNull(cl);
        Assert.assertNotNull(manipulator.getManipulationMetadata());
        Assert.assertFalse(manipulator.getInnerClasses().isEmpty());

        System.out.println(manipulator.getManipulationMetadata());

        // The manipulation add stuff to the class.
        Assert.assertTrue(classloader.get(className).length > origin.length);


        boolean found = false;
        Constructor cst = null;
        Constructor[] csts = cl.getDeclaredConstructors();
        for (Constructor cst2 : csts) {
            System.out.println(Arrays.asList(cst2.getParameterTypes()));
            if (cst2.getParameterTypes().length == 1 &&
                    cst2.getParameterTypes()[0].equals(InstanceManager.class)) {
                found = true;
                cst = cst2;
            }
        }
        Assert.assertTrue(found);

        // We still have the empty constructor
        found = false;
        csts = cl.getDeclaredConstructors();
        for (Constructor cst1 : csts) {
            System.out.println(Arrays.asList(cst1.getParameterTypes()));
            if (cst1.getParameterTypes().length == 0) {
                found = true;
            }
        }
        Assert.assertTrue(found);

        // Check the POJO interface
        Assert.assertTrue(Arrays.asList(cl.getInterfaces()).contains(Pojo.class));

        InstanceManager im = Mockito.mock(InstanceManager.class);
        cst.setAccessible(true);
        Object pojo = cst.newInstance(new Object[]{im});
        Assert.assertNotNull(pojo);
        Assert.assertTrue(pojo instanceof Pojo);
        Method method = cl.getMethod("doSomething", new Class[0]);
        Assert.assertTrue(((Boolean) method.invoke(pojo, new Object[0])).booleanValue());

    }

    @Test
    public void testInnerClasses() throws IOException, ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
        Manipulator manipulator = new Manipulator(this.getClass().getClassLoader());
        String className = "test.inner.ComponentWithInnerClasses";
        ManipulatedClassLoader classloader = manipulate(className, manipulator);


        Class clazz = classloader.findClass(className);
        Assert.assertNotNull(clazz);
        Assert.assertNotNull(manipulator.getManipulationMetadata());
        Assert.assertFalse(manipulator.getInnerClasses().isEmpty());
        // We should have found only 2 inner classes.
        assertThat(manipulator.getInnerClasses().size()).isEqualTo(3);

        // Check that all inner classes are manipulated.
        InstanceManager im = Mockito.mock(InstanceManager.class);
        Constructor constructor = clazz.getDeclaredConstructor(InstanceManager.class);
        constructor.setAccessible(true);
        Object pojo = constructor.newInstance(im);
        Assert.assertNotNull(pojo);
        Assert.assertTrue(pojo instanceof Pojo);
        Method method = clazz.getMethod("doSomething", new Class[0]);
        String result = (String) method.invoke(pojo);
        assertEquals(result, "foofoofoofoo");
    }

    @Test
    public void testDoubleManipulation() throws IOException, ClassNotFoundException, NoSuchMethodException,
            IllegalAccessException, InvocationTargetException, InstantiationException {
        Manipulator manipulator = new Manipulator(this.getClass().getClassLoader());
        String className = "test.inner.ComponentWithInnerClasses";
        ManipulatedClassLoader classloader = manipulate(className, manipulator);

        manipulator = new Manipulator(this.getClass().getClassLoader());
        classloader = manipulate(className, manipulator, classloader);

        Class clazz = classloader.findClass(className);
        Assert.assertNotNull(clazz);
        Assert.assertNotNull(manipulator.getManipulationMetadata());
        Assert.assertFalse(manipulator.getInnerClasses().isEmpty());
        // We should have found only 2 inner classes.
        assertThat(manipulator.getInnerClasses().size()).isEqualTo(3);

        // Check that all inner classes are manipulated.
        InstanceManager im = Mockito.mock(InstanceManager.class);
        Constructor constructor = clazz.getDeclaredConstructor(InstanceManager.class);
        constructor.setAccessible(true);
        Object pojo = constructor.newInstance(im);
        Assert.assertNotNull(pojo);
        Assert.assertTrue(pojo instanceof Pojo);
        Method method = clazz.getMethod("doSomething", new Class[0]);
        String result = (String) method.invoke(pojo);
        assertEquals(result, "foofoofoofoo");
    }

    @Test
    public void testThatManipulationMetadataContainsTheInnerClasses() throws IOException, ClassNotFoundException,
            NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
        Manipulator manipulator = new Manipulator(this.getClass().getClassLoader());
        String className = "test.inner.ComponentWithInnerClasses";
        manipulate(className, manipulator);

        assertThat(manipulator.getInnerClasses().size()).isEqualTo(3);

        Element manipulation = manipulator.getManipulationMetadata();
        System.out.println(manipulation);
        Element[] inners = manipulation.getElements("inner");
        assertThat(inners.length).isEqualTo(3);

        Element inner = getInnerClassMetadataByName(inners, "MyInnerWithANativeMethod");
        assertThat(inner).isNotNull();
        assertThat(getMethodByName(inner.getElements("method"), "foo")).isNotNull();

        inner = getInnerClassMetadataByName(inners, "MyInnerClass");
        assertThat(inner).isNotNull();
        assertThat(getMethodByName(inner.getElements("method"), "foo")).isNotNull();

        inner = getInnerClassMetadataByName(inners, "1");
        assertThat(inner).isNotNull();
        assertThat(getMethodByName(inner.getElements("method"), "compute")).isNotNull();
    }

    @Test
    public void testThatTheClassContainsTheFlagsForTheInnerMethods() throws IOException, ClassNotFoundException,
            NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException, NoSuchFieldException {
        Manipulator manipulator = new Manipulator(this.getClass().getClassLoader());
        String className = "test.inner.ComponentWithInnerClasses";
        ManipulatedClassLoader classLoader = manipulate(className, manipulator);

        Class clazz = classLoader.findClass(className);

        String flag = "__M" + "MyInnerWithANativeMethod" + "___" + "foo";
        assertThat(clazz.getDeclaredField(flag)).isNotNull();

        flag = "__M" + "MyInnerClass" + "___" + "foo";
        assertThat(clazz.getDeclaredField(flag)).isNotNull();

        flag = "__M" + "1" + "___" + "compute" + "$java_lang_String";
        assertThat(clazz.getDeclaredField(flag)).isNotNull();
    }

    @Test
    public void testThatStaticInnerClassesAreNotManipulated() throws Exception {
        Manipulator manipulator = new Manipulator(this.getClass().getClassLoader());
        String className = "test.inner.ComponentWithInnerClasses";
        ManipulatedClassLoader classLoader = manipulate(className, manipulator);

        Class clazz = classLoader.findClass(className);
        Class inner = findInnerClass(clazz.getClasses(), "MyStaticInnerClass");
        assertThat(inner).isNotNull();
        Method bar = inner.getMethod("bar");
        Object o = inner.newInstance();
        bar.setAccessible(true);
        assertThat(bar).isNotNull();
        assertThat((String) bar.invoke(o)).isEqualTo("bar");
    }

    @Test
    public void testThatAnonymousClassDeclaredInStaticFieldsAreNotManipulated() throws Exception {
        Manipulator manipulator = new Manipulator(this.getClass().getClassLoader());
        String className = "test.inner.ComponentWithInnerClasses";
        ManipulatedClassLoader classLoader = manipulate(className, manipulator);

        Class clazz = classLoader.findClass(className);
        Method method = clazz.getMethod("call");
        assertThat(method).isNotNull();
        assertThat(method.invoke(null)).isEqualTo(1);
    }

    private Class findInnerClass(Class[] classes, String name) {
        for (Class clazz : classes) {
            if (clazz.getName().contains(name)) {
                return clazz;
            }
        }
        return null;
    }

}
TOP

Related Classes of org.apache.felix.ipojo.manipulation.InnerClassAdapterTest

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.