/*
 * Copyright 2014 NAVER Corp.
 *
 * Licensed 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 com.navercorp.pinpoint.plugin.user;

import com.navercorp.pinpoint.bootstrap.instrument.MethodFilters;
import com.navercorp.pinpoint.bootstrap.logging.PLogger;
import com.navercorp.pinpoint.bootstrap.logging.PLoggerFactory;
import com.navercorp.pinpoint.bootstrap.plugin.ProfilerPlugin;
import com.navercorp.pinpoint.bootstrap.plugin.ProfilerPluginSetupContext;
import com.navercorp.pinpoint.bootstrap.plugin.transformer.ClassFileTransformerBuilder;
import com.navercorp.pinpoint.bootstrap.plugin.transformer.MethodTransformerBuilder;
import com.navercorp.pinpoint.bootstrap.plugin.transformer.MethodTransformerProperty;

/**
 * @author jaehong.kim
 *
 */
public class UserPlugin implements ProfilerPlugin, UserConstants {
    private final PLogger logger = PLoggerFactory.getLogger(this.getClass());

    @Override
    public void setup(ProfilerPluginSetupContext context) {
        final UserPluginConfig config = new UserPluginConfig(context.getConfig());
        
        // add user include methods 
        for (String fullQualifiedMethodName : config.getIncludeList()) {
            try {
                addUserIncludeClass(context, fullQualifiedMethodName);
                if(logger.isDebugEnabled()) {
                    logger.debug("Add user include class interceptor {}", fullQualifiedMethodName);
                }
            } catch (Exception e) {
                logger.warn("Failed to add user include class(" + fullQualifiedMethodName + ").", e);
            }
        }
    }

    private void addUserIncludeClass(ProfilerPluginSetupContext context, final String fullQualifiedMethodName) {
        final String className = toClassName(fullQualifiedMethodName);
        final String methodName = toMethodName(fullQualifiedMethodName);
        final ClassFileTransformerBuilder classEditorBuilder = context.getClassFileTransformerBuilder(className);
        MethodTransformerBuilder methodEditorBuilder = classEditorBuilder.editMethods(MethodFilters.name(methodName));
        methodEditorBuilder.property(MethodTransformerProperty.IGNORE_IF_NOT_EXIST);
        methodEditorBuilder.injectInterceptor("com.navercorp.pinpoint.plugin.user.interceptor.UserIncludeMethodInterceptor");
        context.addClassFileTransformer(classEditorBuilder.build());
    }

    String toClassName(String fullQualifiedMethodName) {
        final int classEndPosition = fullQualifiedMethodName.lastIndexOf(".");
        if (classEndPosition <= 0) {
            throw new IllegalArgumentException("invalid full qualified method name(" + fullQualifiedMethodName + "). not found method");
        }

        return fullQualifiedMethodName.substring(0, classEndPosition);
    }

    String toMethodName(String fullQualifiedMethodName) {
        final int methodBeginPosition = fullQualifiedMethodName.lastIndexOf(".");
        if (methodBeginPosition <= 0 || methodBeginPosition + 1 >= fullQualifiedMethodName.length()) {
            throw new IllegalArgumentException("invalid full qualified method name(" + fullQualifiedMethodName + "). not found method");
        }

        return fullQualifiedMethodName.substring(methodBeginPosition + 1);
    }
}
@@ -633,16 +633,265 @@
 .user;%0A%0A
+import java.security.ProtectionDomain;%0A%0Aimport com.navercorp.pinpoint.bootstrap.instrument.InstrumentClass;%0Aimport com.navercorp.pinpoint.bootstrap.instrument.InstrumentException;%0Aimport com.navercorp.pinpoint.bootstrap.instrument.InstrumentMethod;%0A
 import c
@@ -1197,108 +1197,25 @@
 ugin
-SetupContext;%0Aimport com.navercorp.pinpoint.bootstrap.plugin.transformer.ClassFileTransformerBuilder
+InstrumentContext
 ;%0Aim
@@ -1263,44 +1263,34 @@
 gin.
-transformer.MethodTransformerBuilder
+ProfilerPluginSetupContext
 ;%0Aim
@@ -1346,22 +1346,33 @@
 sformer.
-Method
+PinpointClassFile
 Transfor
@@ -1374,24 +1374,16 @@
 nsformer
-Property
 ;%0A%0A/**%0A 
@@ -1714,24 +1714,16 @@
 fig());%0A
-        
 %0A       
@@ -1750,17 +1750,16 @@
  methods
- 
 %0A       
@@ -1931,16 +1931,17 @@
       if
+ 
 (logger.
@@ -2500,32 +2500,33 @@
 ethodName);%0A
+%0A
         
 final ClassF
@@ -2513,22 +2513,27 @@
         
-final 
+context.add
 ClassFil
@@ -2548,215 +2548,458 @@
 rmer
-Builder classEditorBuilder = context.getClassFileTransformerBuilder(className);%0A        MethodTransformerBuilder methodEditorBuil
+(className, new PinpointClassFileTransformer() %7B%0A%0A            @Override%0A            public byte%5B%5D transform(ProfilerPluginInstrumentContext instrumentContext, ClassLoa
 der
- =
  class
-EditorBuilder.editMethods(MethodFilters.name(methodName)
+Loader, String className, Class%3C?%3E classBeingRedefined, ProtectionDomain protectionDomain, byte%5B%5D classfileBuffer) throws InstrumentException %7B%0A                InstrumentClass target = instrumentContext.getInstrumentClass(classLoader, className, classfileBuffer
 );%0A
+%0A 
         
 meth
@@ -2998,277 +2998,567 @@
     
-methodEditorBuilder.property(MethodTransformerProperty.IGNORE_IF_NOT_EXIST);%0A        methodEditorBuilder.injectInterceptor(%22com.navercorp.pinpoint.plugin.user.interceptor.UserIncludeMethodInterceptor%22);%0A        context.addClassFileTransformer(classEditorBuilder.build()
+       for (InstrumentMethod method : target.getDeclaredMethods(MethodFilters.name(methodName))) %7B%0A                    try %7B%0A                        method.addInterceptor(%22com.navercorp.pinpoint.plugin.user.interceptor.UserIncludeMethodInterceptor%22);%0A                    %7D catch (Exception e) %7B%0A                        if (logger.isWarnEnabled()) %7B%0A                            logger.warn(%22Unsupported method %22 + method, e);%0A                        %7D%0A                    %7D%0A                %7D%0A%0A                return target.toBytecode();%0A            %7D%0A        %7D
 );%0A 

change plugin.