/**************************************************************************************
 * Copyright (c) Jonas Bonér, Alexandre Vasseur. All rights reserved.                 *
 * http://aspectwerkz.codehaus.org                                                    *
 * ---------------------------------------------------------------------------------- *
 * The software in this package is published under the terms of the LGPL license      *
 * a copy of which has been included with this distribution in the license.txt file.  *
 **************************************************************************************/
package examples.logging;

import org.codehaus.aspectwerkz.extension.hotswap.HotSwapClient;
import org.codehaus.aspectwerkz.SystemLoader;
import org.codehaus.aspectwerkz.definition.*;

import java.io.File;

/**
 * Sample on how to use Runtime Weaving programmatically. <p/>Note: to debug from within the IDE
 * with the WeavingClassLoader it is mandatory to use the -Daspectwerkz.transform.forceWCL=true
 * option so that AspectWerkz gets loaded in the WeavingClassLoader and not in the (parallel) system
 * class loader.
 * 
 * @author <a href="mailto:alex@gnilux.com">Alexandre Vasseur </a>
 */
public class HotSwapTarget {

    private int m_counter1;

    private int m_counter2;

    public static boolean showStack = false;

    public int getCounter() {
        System.out.println("getCounter()");
        return m_counter1;
    }

    public void increment() {
        System.out.println("increment()");
        m_counter2 = m_counter2 + 1;
    }

    public static void toLog1WithStack() {
        showStack = true;
        toLog1();
        showStack = false;
    }

    public static void toLog1() {
        System.out.println("toLog1()");
        new HotSwapTarget().toLog2("parameter");
    }

    private void toLog2(java.lang.String arg) {
        System.out.println("  toLog2()");
        new HotSwapTarget().toLog3();
    }

    private String toLog3() {
        System.out.println("    toLog3()");
        if (showStack) {
            (new Exception("expected exception to show stack trace..."))
                    .printStackTrace(System.out);
        }
        return "result";
    }

    public static void demo1() throws Throwable {
        // regular calls
        System.out.println("\n\n== before activation ==");
        HotSwapTarget.toLog1();
        HotSwapTarget target = new HotSwapTarget();
        target.increment();
        target.getCounter();
        // show stack
        HotSwapTarget.toLog1WithStack();
        Thread.sleep(3000);
        System.out.println("\n\n\n");

        // lets add a system
        SystemLoader.deploySystemDefinitions(HotSwapTarget.class.getClassLoader(), XmlParser
                .parseNoCache(HotSwapTarget.class.getClassLoader(), (new File(
                    "src/samples/hotdeployed.xml")).toURL()), true);

        // add new pointcuts
        //JavaLoggingAspect.addPointcutForLoggingAdvice("execution(*
        // examples.logging.HotSwapTarget.toLog1())",
        // "runtimePCToLog1");
        //JavaLoggingAspect.addPointcutForLoggingAdvice("call(*->*
        // examples.logging.HotSwapTarget.toLog2(..))",
        // "CALLruntimePCToLog2");
        // call HotSwap for runtime weaving
        //HotSwapClient.hotswap(HotSwapTarget.class);

        // hotswapped calls
        System.out.println("\n\n== after activation of toLog1() ==");
        HotSwapTarget.toLog1();
        target.increment();
        target.getCounter();
        // show stack

        HotSwapTarget.toLog1WithStack();
        Thread.sleep(3000);
        System.out.println("\n\n\n");

        // add new pointcuts
        //addPointcutForLoggingAdvice("* examples.logging.HotSwapTarget.toLog3(int)",
        // "runtimePCToLog3b");
        JavaLoggingAspect.addPointcutForLoggingAdvice(
            "execution(* examples.logging.HotSwapTarget.toLog2(..))",
            "runtimePCToLog2");
        // call HotSwap for runtime weaving
        HotSwapClient.hotswap(HotSwapTarget.class);

        // hotswapped calls
        System.out.println("\n\n== after second activation, same instance ==");
        HotSwapTarget.toLog1();
        target.increment();
        target.getCounter();
        //        System.out.println("\n== after second activation, other instance ==");
        //        HotSwapTarget.toLog1();
        //        target = new HotSwapTarget();
        //        target.increment();
        //        target.getCounter();

        // remove
        JavaLoggingAspect.removePointcutForLoggingAdvice("", "runtimePCToLog1");
        JavaLoggingAspect.removePointcutForLoggingAdvice("", "runtimePCToLog2");
        JavaLoggingAspect.removePointcutForLoggingAdvice("", "CALLruntimePCToLog2");
        System.out.println("\n\n== after removal of pc defs ==");
        HotSwapTarget.toLog1();

        // call HotSwap for runtime weaving
        HotSwapClient.hotswap(HotSwapTarget.class);
        System.out.println("\n\n== after un-weaving of removed pc defs ==");
        HotSwapTarget.toLog1();
        // show stack
        HotSwapTarget.toLog1WithStack();
        Thread.sleep(3000);
        System.out.println("\n\n\n");
    }

    public static void main(String a[]) throws Throwable {
        demo1();
        benchHotSwap();
        System.exit(0);
    }

    public static void benchHotSwap() {
        if (true) {
            return;
        }
        int loop = 100;
        long ts = System.currentTimeMillis();
        for (int i = 0; i < loop; i++) {
            HotSwapClient.hotswap(HotSwapTarget.class);
        }
        System.out.println("perSwap without def change = "
            + (System.currentTimeMillis() - ts)
            / loop);
        ts = System.currentTimeMillis();
        for (int i = 0; i < loop; i++) {
            JavaLoggingAspect.addPointcutForLoggingAdvice(
                "execution(* examples.logging.HotSwapTarget.toLog1())",
                "runtimePCToLog1");
            HotSwapClient.hotswap(HotSwapTarget.class);
            JavaLoggingAspect.removePointcutForLoggingAdvice("", "runtimePCToLog1");
        }
        System.out.println("perSwap with def change = " + (System.currentTimeMillis() - ts) / loop);
    }

}