001/**
002 *
003 * Copyright (c) 2014, the Railo Company Ltd. All rights reserved.
004 *
005 * This library is free software; you can redistribute it and/or
006 * modify it under the terms of the GNU Lesser General Public
007 * License as published by the Free Software Foundation; either 
008 * version 2.1 of the License, or (at your option) any later version.
009 * 
010 * This library is distributed in the hope that it will be useful,
011 * but WITHOUT ANY WARRANTY; without even the implied warranty of
012 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
013 * Lesser General Public License for more details.
014 * 
015 * You should have received a copy of the GNU Lesser General Public 
016 * License along with this library.  If not, see <http://www.gnu.org/licenses/>.
017 * 
018 **/
019package lucee.runtime.instrumentation;
020
021import java.lang.instrument.Instrumentation;
022import java.lang.management.ManagementFactory;
023import java.lang.management.RuntimeMXBean;
024import java.lang.reflect.Field;
025import java.lang.reflect.InvocationTargetException;
026import java.lang.reflect.Method;
027
028import lucee.commons.io.SystemUtil;
029import lucee.commons.io.res.Resource;
030import lucee.commons.io.res.ResourcesImpl;
031import lucee.commons.lang.ClassUtil;
032import lucee.commons.lang.ExceptionUtil;
033import lucee.commons.lang.SystemOut;
034
035import org.objectweb.asm.ClassReader;
036
037@SuppressWarnings("UseOfSunClasses")
038public class InstrumentationFactory {
039        
040        private static Instrumentation inst;
041        private static boolean doInit=true;
042        
043        public static synchronized Instrumentation getInstance() {
044                getInstance("lucee","lucee.runtime.instrumentation.Agent");
045                if(inst==null)getInstance("railo","railo.runtime.instrumentation.Agent");
046                
047                return inst;
048        }
049        
050        private static synchronized Instrumentation getInstance(String name,String className) {
051                if(doInit) {
052                        doInit=false;
053                        
054                        Class agent = ClassUtil.loadClass(className,null);
055                        if(agent==null) {
056                                SystemOut.printDate("missing class "+className);
057                                return null;
058                        }
059                        
060                        // if Agent was loaded at startup there is already a Instrumentation
061                        inst=getInstrumentation(agent);
062                        
063                        // try to load Agent
064                        if(inst==null) {
065                                SystemOut.printDate("class "+className+".getInstrumentation() is not returning a Instrumentation");
066                                try {
067                                        String id=getPid();
068                                        String path=getResourcFromLib(name,className).getAbsolutePath();
069                                        
070                                        Class vmClass = ClassUtil.loadClass("com.sun.tools.attach.VirtualMachine");
071                                        Object vmObj=attach(vmClass,id);
072                                        loadAgent(vmClass,vmObj,path);
073                                        detach(vmClass,vmObj);
074                                } 
075                                catch (Throwable t) {
076                                        ExceptionUtil.rethrowIfNecessary(t);
077                                        //t.printStackTrace();
078                                        return null;
079                                }
080                                inst=getInstrumentation(agent);
081                        }
082                        
083                        if(inst!=null)SystemOut.printDate("java.lang.instrument.Instrumentation is used to reload class files");
084                                
085                }
086                return inst;
087        }
088
089        private static Instrumentation getInstrumentation(Class agent) {
090                try {
091                        Method getInstrumentation = agent.getMethod("getInstrumentation", new Class[0]);
092                        return (Instrumentation) getInstrumentation.invoke(null, new Object[0]);
093                } 
094                catch (Throwable t) {
095                        ExceptionUtil.rethrowIfNecessary(t);
096                        t.printStackTrace();
097                        return null;
098                }
099        }
100
101        private static Object attach(Class vmClass, String id) throws SecurityException, NoSuchMethodException, IllegalArgumentException, IllegalAccessException, InvocationTargetException {
102                Method attach = vmClass.getMethod("attach", new Class[]{String.class});
103                return attach.invoke(null, new Object[]{id});
104        }
105        
106        private static void loadAgent(Class vmClass, Object vmObj, String path) throws SecurityException, NoSuchMethodException, IllegalArgumentException, IllegalAccessException, InvocationTargetException {
107                Method loadAgent = vmClass.getMethod("loadAgent", new Class[]{String.class});
108                loadAgent.invoke(vmObj, new Object[]{path});
109        }
110        
111        private static void detach(Class vmClass, Object vmObj) throws SecurityException, NoSuchMethodException, IllegalArgumentException, IllegalAccessException, InvocationTargetException {
112                Method detach = vmClass.getMethod("detach", new Class[]{});
113                detach.invoke(vmObj, new Object[]{});
114        }
115        
116        private static Resource getResourcFromLib(String name,String className) {
117                Resource[] pathes = SystemUtil.getClassPathes();
118                Resource res = null;
119                String fileName=null;
120                if(pathes!=null)for(int i=0;i<pathes.length;i++){
121                        fileName=pathes[i].getName();
122                        if(fileName.equalsIgnoreCase(name+"-instrumentation.jar") || fileName.equalsIgnoreCase(name+"-inst.jar")) {
123                                res=pathes[i];
124                                break;
125                        }
126                }
127                
128                if(res==null) {
129                        Class agent = ClassUtil.loadClass(className,null);
130                        if(agent!=null)res=getResourcFromLib(agent);
131                        else res=getResourcFromLib(ClassReader.class);
132                        
133                }
134                return res;
135        }
136
137        private static Resource getResourcFromLib(Class clazz) {
138                String path=clazz.getClassLoader().getResource(".").getFile();
139                Resource dir = ResourcesImpl.getFileResourceProvider().getResource(path);
140                Resource res = dir.getRealResource("lucee-instrumentation.jar");
141                if(!res.exists())res=dir.getRealResource("lucee-inst.jar");
142                if(!res.exists())res=null;
143                return res;
144        }
145        private static String getPid() throws SecurityException, NoSuchFieldException, IllegalArgumentException, IllegalAccessException, NoSuchMethodException, InvocationTargetException {
146                RuntimeMXBean mxbean = ManagementFactory.getRuntimeMXBean();
147            Field jvmField = mxbean.getClass().getDeclaredField("jvm");
148
149            jvmField.setAccessible(true);
150            sun.management.VMManagement management = (sun.management.VMManagement) jvmField.get(mxbean);
151            Method method = management.getClass().getDeclaredMethod("getProcessId");
152            method.setAccessible(true);
153            Integer processId = (Integer) method.invoke(management);
154
155            return processId.toString();
156        }
157}