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}