1

私のQAチームは、アプリケーションクロックを移動する必要がある、ビジネスライフサイクルテスト(つまり、エージング、期限切れ、期限、期限超過など)を行っています。調整されたクロック(自分が制御する)を参照するようにすべてのコードを変更できます。問題は、(Web)アプリケーションが現在の時刻に依存し、またはSystem.currentTimeMillis()を介して直接または間接的に使用するいくつかのサードパーティツール(Spring Batch、Activitiなど)を使用することです。DateCalendar

オプション1-春のAOP。このオプションを試したところ、SpringでロードされたBeanのみをインストルメントしているように見えました(?)SystemクラスはSpringフレームワークの外部でロードされたため、インストルメントできませんでした。

オプション2-JMockit。JMockitjarをJUnitの前に置くのはやや型破りです。

オプション3-Java6インストルメンテーションを使用します(オプション1とオプション2の間の共通部分)。基本に戻ります...(以下の関連コードを見つけてください)。

ただし、テストコードのアサートは常に失敗します。

私は3つのオプションすべてで障害にぶつかりました。これまで誰もこれを行ったことがないとは信じられませんが、合理的な解決策も見つかりません。

前もって感謝します。

public class InstrumentationAgent {
    private static Instrumentation instrumentation = null;


    /**
     * JVM hook to dynamically load InstrumentationAgent at runtime.
     * 
     * The agent class may have an agentmain method for use when the agent is
     * started after VM startup.
     * 
     * @param agentArgument
     * @param instrumentation
     */
    public static void agentmain(String agentArgument, Instrumentation instrumentation) {
        InstrumentationAgent.instrumentation = instrumentation;
    }

    /**
     * Programmatic hook to dynamically load modified byte codes. This method initializes/load the agent if necessary.
     * 
     * @param definitions
     * @throws Exception
     */
    public static void redefineClasses(ClassDefinition... definitions) throws Exception {
        if (InstrumentationAgent.instrumentation == null) {
            loadAgent();
        }

        InstrumentationAgent.instrumentation.redefineClasses(definitions);
    }

    private synchronized static void loadAgent() throws Exception {
        if (InstrumentationAgent.instrumentation != null) {
            return;
        }

        // Build the agent.jar file
        final File jarFile = File.createTempFile("agent", ".jar");
        jarFile.deleteOnExit();

        final Manifest manifest = new Manifest();
        final Attributes mainAttributes = manifest.getMainAttributes();
        mainAttributes.put(Attributes.Name.MANIFEST_VERSION, "1.0");
        mainAttributes.put(new Attributes.Name("Agent-Class"), InstrumentationAgent.class.getName());
        mainAttributes.put(new Attributes.Name("Can-Retransform-Classes"), "true");
        mainAttributes.put(new Attributes.Name("Can-Redefine-Classes"), "true");

        final JarOutputStream jos = new JarOutputStream(new FileOutputStream(jarFile), manifest);
        final JarEntry agent = new JarEntry(InstrumentationAgent.class.getName().replace('.', '/') + ".class");
        jos.putNextEntry(agent);
        final ClassPool pool = ClassPool.getDefault();
        final CtClass ctClass = pool.get(InstrumentationAgent.class.getName());
        jos.write(ctClass.toBytecode());
        jos.closeEntry();
        jos.close();

        // Attach to VM and load the agent
        VirtualMachine vm = VirtualMachine.attach(getPidFromRuntimeMBean());
        vm.loadAgent(jarFile.getAbsolutePath());
        vm.detach();
    }

    private static String getPidFromRuntimeMBean() throws Exception {
        RuntimeMXBean mxbean = ManagementFactory.getRuntimeMXBean();
        Field jvmField = mxbean.getClass().getDeclaredField("jvm");

        jvmField.setAccessible(true);
        VMManagement management = (VMManagement) jvmField.get(mxbean);
        Method method = management.getClass().getDeclaredMethod("getProcessId");
        method.setAccessible(true);
        Integer processId = (Integer) method.invoke(management);

        return processId.toString();
    }

}



public class SystemTimeInstrumentation {
    private static long timeAdjustment = 200000L;
    private static byte[] originalClassByteArray;

    public static void startAdjustedClock() {
        ClassPool pool = ClassPool.getDefault();

        CtClass ctClass = null;
        byte[] instrumentedClassByteArray = null;
        try {
            originalClassByteArray = pool.get(System.class.getName()).toBytecode();
            ctClass = pool.makeClass(new java.io.ByteArrayInputStream(originalClassByteArray), false);
            CtMethod ctMethod = ctClass.getDeclaredMethod("currentTimeMillis");

            ctMethod.setBody("return 0L;");

            instrumentedClassByteArray = ctClass.toBytecode();

        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (CannotCompileException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (NotFoundException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (Exception e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } finally {
            if (ctClass != null) {
                ctClass.detach();
            }
        }

        try {
            InstrumentationAgent.redefineClasses(new ClassDefinition[] { new ClassDefinition(System.class,
                    instrumentedClassByteArray) });
        } catch (Exception e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }

    public static void stopAdjustedClock() {
        if (originalClassByteArray == null) {
            throw new RuntimeException("The stopAdjustedClock() called before startAdjustedClock()");
        } else {
            try {
                InstrumentationAgent.redefineClasses(new ClassDefinition[] { new ClassDefinition(System.class,
                        originalClassByteArray) });
            } catch (Exception e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
            originalClassByteArray = null;
        }
    }


public class SystemTimeInstrumentationTest extends TestCase {

    @Test 
    public void testModifiedClock() throws Exception {
        long unmodifiedTime = System.currentTimeMillis();
        SystemTimeInstrumentation.startAdjustedClock();
        long modifiedTime = System.currentTimeMillis();
        SystemTimeInstrumentation.stopAdjustedClock();

        assertTrue("The difference should me more than 200000", (modifiedTime-unmodifiedTime)>200000L);

    }

}
4

0 に答える 0