/** * PreparedStatement用にパターン別のコードを埋め込む * * @param ctClass クラス * @param method メソッド * @param bindValCode バインド変数取得用コード * @param explainCodeTemplate 実行計画取得用コード * @throws CannotCompileException コード埋め込みに失敗した場合 */ public static void convertPreparedMethod( final CtClass ctClass, final CtBehavior method, final String bindValCode, final String explainCodeTemplate) throws CannotCompileException { String className = ctClass.getName(); String methodName = method.getName(); className = className.substring(className.lastIndexOf('.') + 1); // setメソッドの引数の1番目がintのときのみ、実行計画取得用処理、バインド引数取得処理を追加する try { CtClass[] paramTypes; paramTypes = method.getParameterTypes(); if (paramTypes.length >= 1 && "int".equals(paramTypes[0].getName())) { // 実行計画取得用PreparedStatementのsetXXXを適したメソッド名に変更する String explainCode = explainCodeTemplate.replaceAll(REPLACETARGET_OF_PLANPREPARED, methodName); // 前処理を埋め込む String key = "javelin.jdbc.instrument.JdbcJavelinConverter.ModifiedMethodLabel"; String message = JdbcJavelinMessages.getMessage(key, className, methodName); SystemLogger.getInstance().info(message); method.insertBefore(bindValCode + explainCode); } } catch (NotFoundException e) { SystemLogger.getInstance().warn(e); } }
/** * ログ取得コードをThrowableのcatch節として追加する * * @param pool Statementを含むプール * @param ctClass PreparedStatementを実装するクラス * @param behaviour behaviour * @throws CannotCompileException コンパイルができないときの例外 */ public static void convertCatch( final ClassPool pool, final CtClass ctClass, final CtBehavior behaviour) throws CannotCompileException { try { CtClass throwable = pool.get("java.lang.Throwable"); // メソッドの定義がない場合は実行しない。 final int MODIFIER = behaviour.getModifiers(); if (Modifier.isAbstract(MODIFIER)) { return; } // JavelinLogger#writeExceptionLogの呼び出しコードを作成する。 StringBuffer code = new StringBuffer(); // 後処理(例外場合) code.append(JAVELIN_RECORDER_NAME); code.append(".postProcessNG("); code.append("$e"); code.append(");"); // 例外を再throwする。 code.append("throw $e;"); // ログ取得コードをThrowableのcatch節として追加する。 behaviour.addCatch(code.toString(), throwable); } catch (NotFoundException nfe) { SystemLogger.getInstance().warn(nfe); } }
/** * ジョブ開始時、終了時、失敗時の処理を埋め込む。 * * @param ctMethod 埋め込み対象のメソッド * @throws CannotCompileException */ private void insertProcessesForJob(final CtBehavior ctMethod) throws CannotCompileException { ctMethod.insertBefore(MapReduceTaskMonitor.class.getName() + ".preProcess(this);"); ctMethod.insertAfter(MapReduceTaskMonitor.class.getName() + ".postProcess(this);"); ctMethod.addCatch( "{" + MapReduceTaskMonitor.class.getName() + ".postProcessNG(this, $e); $e.printStackTrace(); throw $e;}", throwableClass_); }
/** * PreparedStatement#close用にコードを埋め込む * * @param ctClass PreparedStatementを実装するクラス * @param method closeメソッド * @throws CannotCompileException コード埋め込みに失敗した場合 */ public static void convertPreparedMethodClose(final CtClass ctClass, final CtBehavior method) throws CannotCompileException { String className = ctClass.getName(); className = className.substring(className.lastIndexOf('.') + 1); // 前処理を埋め込む String key = "javelin.jdbc.instrument.JdbcJavelinConverter.ModifiedMethodLabel"; String message = JdbcJavelinMessages.getMessage(key, className, method.getName()); SystemLogger.getInstance().info(message); method.insertAfter(BCI_METHOD_PLANFORPREPARED_CLOSE); }
/** * 指定したクラスに指定したメソッドが存在するかどうかを調べます。 * * @param targetClass メソッドの存在を調べるクラス * @param methodName メソッド名 * @return メソッドが存在する場合は <code>true</code> 、存在しない場合は <code>false</code> */ public static boolean hasBehavior(final CtClass targetClass, final String methodName) { boolean method = false; CtBehavior[] declaredBehaviors = targetClass.getDeclaredBehaviors(); for (CtBehavior behavior : declaredBehaviors) { String behaviorName = behavior.getName(); if (methodName.equals(behaviorName)) { method = true; break; } } return method; }
/** * PreparedStatementのexecuteメソッドに、 バインド引数保存用ArrayList初期化処理を追加する。 * * @param ctClass 変換対象のクラス。 * @param method メソッド。 * @throws CannotCompileException javassistがコンパイルに失敗した場合。 */ private static void convertExecuteMethod(final CtClass ctClass, final CtBehavior method) throws CannotCompileException { String className = ctClass.getName(); className = className.substring(className.lastIndexOf('.') + 1); // 前処理を埋め込む String key = "javelin.jdbc.instrument.JdbcJavelinConverter.ModifiedMethodLabel"; String message = JdbcJavelinMessages.getMessage(key, className, method.getName()); SystemLogger.getInstance().info(message); method.insertAfter( "this.jdbcJavelinBindValIndex_ = 0;" + "this.flagForPlanStmt_ = false;", true); }
/** * フィールドからSQLを削除する * * @param ctClass クラス * @param method メソッド * @throws CannotCompileException コンパイル不可時のエラー */ public static void delSqlFromField(final CtClass ctClass, final CtBehavior method) throws CannotCompileException { // 実行しているSQL文を追加する用コードを追加する(Statementの方) String addSqlCode = "if(this != null) this.jdbcJavelinSql_.clear();"; method.insertAfter(addSqlCode); if (SystemLogger.getInstance().isDebugEnabled()) { String tagKey = "javelin.jdbc.instrument.JdbcJavelinConverter.JDBCJavelinTag"; String jdbcJavelinTag = JdbcJavelinMessages.getMessage(tagKey); String messageKey = "javelin.jdbc.instrument.JdbcJavelinConverter.SQLDeletedLabel3"; String logMessage = JdbcJavelinMessages.getMessage(messageKey, method.getName()); SystemLogger.getInstance().debug(jdbcJavelinTag + logMessage); } }
/** * タスク開始前の処理を埋め込む。 * * @param ctMethod 埋め込み対象のメソッド * @throws CannotCompileException */ private void insertPreProcessTask(final CtBehavior ctMethod) throws CannotCompileException { ctMethod.instrument( new ExprEditor() { public void edit(MethodCall m) throws CannotCompileException { String methodName = m.getMethodName(); String className = m.getClassName(); if ((methodName.equals("invokeRemotely") || methodName.equals("invokeRemotelyInFuture")) && className.equals("org.infinispan.remoting.rpc.RpcManager")) { m.replace( "{String address = $0.getAddress().toString();" + MapReduceTaskMonitor.class.getName() + ".preProcessTaskForInvokeRemotely(this, address);" + "$_ = $proceed($$);}"); } else if (methodName.equals("invokeRemotelyInFuture") && className.equals("org.infinispan.remoting.rpc.RpcManager")) { m.replace( "{String addressStr = address.toString();" + MapReduceTaskMonitor.class.getName() + ".preProcessTaskForInvokeRemotelyInFuture(this, addressStr);" + "$_ = $proceed($$);}"); } else if (methodName.equals("perform") && className.equals("org.infinispan.commands.read.MapReduceCommand")) { m.replace( "{String address = rpc.getAddress().toString();" + MapReduceTaskMonitor.class.getName() + ".preProcessTask(this, address);" + "$_ = $proceed($$);}"); } } }); }
private static void addSqlToFieldCon(final CtClass ctClass, final CtBehavior method) throws CannotCompileException { String addSqlCode = JdbcJavelinRecorder.class.getName() + ".postPrepareStatement($0, $1, $_, \"" + method.getName() + "\");"; method.insertAfter(addSqlCode); String key = "javelin.jdbc.instrument.JdbcJavelinConverter.ModifiedMethodLabel"; String message = JdbcJavelinMessages.getMessage(key, ctClass.getName(), method.getName()); SystemLogger.getInstance().info(message); if (SystemLogger.getInstance().isDebugEnabled()) { String tegKey = "javelin.jdbc.instrument.JdbcJavelinConverter.JDBCJavelinTag"; String jdbcJavelinTag = JdbcJavelinMessages.getMessage(tegKey); String messageKey = "javelin.jdbc.instrument.JdbcJavelinConverter.SQLAddedLabel1"; String logMessage = JdbcJavelinMessages.getMessage(messageKey, method.getName()); SystemLogger.getInstance().debug(jdbcJavelinTag + logMessage); } }
/** * StatsJavelinによる計測コードを埋め込む。 * * @param ctClass クラス * @param method メソッド * @throws CannotCompileException メソッドの開始位置と終了位置にコードを埋め込めなかった場合 */ public static void addRecordCode(final CtClass ctClass, final CtBehavior method) throws CannotCompileException { // 前処理ログコードを作る StringBuffer callPreProcessCodeBuffer = new StringBuffer(); callPreProcessCodeBuffer.append(JAVELIN_RECORDER_NAME); CtClass[] parameterTypes = null; boolean paramZero = false; try { parameterTypes = method.getParameterTypes(); } catch (NotFoundException ex) { SystemLogger.getInstance().warn("", ex); } if (parameterTypes != null && (parameterTypes.length > 0)) { callPreProcessCodeBuffer.append(".preProcessParam("); callPreProcessCodeBuffer.append("$0"); callPreProcessCodeBuffer.append(", $args);"); } else { callPreProcessCodeBuffer.append(".preProcessSQLArgs("); callPreProcessCodeBuffer.append("$0"); callPreProcessCodeBuffer.append(", this.getJdbcJavelinSql().toArray());"); paramZero = true; } String callPreProcessCode; callPreProcessCode = callPreProcessCodeBuffer.toString(); // 後処理ログコードを作る StringBuffer callPostProcessCodeBuffer = new StringBuffer(); callPostProcessCodeBuffer.append(JAVELIN_RECORDER_NAME); // Recorderにて実行計画取得にStatemen、クラス名、メソッド名が必要 callPostProcessCodeBuffer.append(".postProcessOK($0"); if (paramZero == false) { // 引数の数をpostProcessOKに渡す callPostProcessCodeBuffer.append(", 1"); // 引数1以上 } else { callPostProcessCodeBuffer.append(", 0"); // 引数0 } callPostProcessCodeBuffer.append(");"); String returnPostProcessCode = callPostProcessCodeBuffer.toString(); // JavelinErrorLogger.getInstance().log("modified class:" + className); method.insertBefore(callPreProcessCode); method.insertAfter(returnPostProcessCode); }
private static void addSqlToFieldStat(final CtClass ctClass, final CtBehavior method) throws CannotCompileException { try { CtClass[] parameterTypes = method.getParameterTypes(); if (!(parameterTypes == null) && (parameterTypes.length > 0)) { // 実行しているSQL文を追加する用コードを追加する(StatementのaddBatchメソッド用) String addSqlCode = "if(this != null) this.jdbcJavelinSql_.add($1);"; method.insertAfter(addSqlCode); if (SystemLogger.getInstance().isDebugEnabled()) { String tagKey = "javelin.jdbc.instrument.JdbcJavelinConverter.JDBCJavelinTag"; String jdbcJavelinTag = JdbcJavelinMessages.getMessage(tagKey); String messageKey = "javelin.jdbc.instrument.JdbcJavelinConverter.SQLAddedLabel2"; String logMessage = JdbcJavelinMessages.getMessage(messageKey, method.getName()); SystemLogger.getInstance().debug(jdbcJavelinTag + logMessage); } } } catch (NotFoundException e) { SystemLogger.getInstance().warn("", e); } }
/** * Statementを実装するクラスに計測コードを埋め込む。 * * @param pool クラスプール * @param ctClass Statementを実装するクラス * @param method 埋め込み対象メソッド * @throws CannotCompileException コード埋め込みに失敗した場合 */ public static void convertStatementMethod( final ClassPool pool, final CtClass ctClass, final CtBehavior method) throws CannotCompileException { // StatsJavelinによる計測コードを埋め込む(Statementの方用) addRecordCode(ctClass, method); convertCatch(pool, ctClass, method); if (SystemLogger.getInstance().isDebugEnabled()) { String tegKey = "javelin.jdbc.instrument.JdbcJavelinConverter.JDBCJavelinTag"; String jdbcJavelinTag = JdbcJavelinMessages.getMessage(tegKey); String messageKey = "javelin.jdbc.instrument.JdbcJavelinConverter.ConvertLabel"; String message = JdbcJavelinMessages.getMessage(messageKey, method.getName()); SystemLogger.getInstance().debug(jdbcJavelinTag + message); } }
/** * ジョブ終了後の処理を埋め込む * * @param ctMethod 埋め込む対象のメソッド * @throws CannotCompileException */ private void insertPostProcess(final CtBehavior ctMethod) throws CannotCompileException { ctMethod.instrument( new ExprEditor() { public void edit(MethodCall m) throws CannotCompileException { String className = m.getClassName(); String methodName = m.getMethodName(); if (className.equals("org.infinispan.remoting.rpc.RpcManager") && methodName.equals("invokeRemotely") || className.equals( "org.infinispan.distexec.mapreduce." + "MapReduceTask.MapReduceFuture") && methodName.equals("get")) { m.replace( "{" + "$_ = $proceed($$);" + "java.util.Map map2 = (java.util.Map)$_;" + "String[] keys = new String[0];" + "java.util.Set keySet = map2.keySet();" + "java.util.Map newMap = new java.util.HashMap();" + "java.util.Iterator it = keySet.iterator();" + "while (it.hasNext()) {" + "org.infinispan.remoting.transport.Address key = " + "(org.infinispan.remoting.transport.Address)it.next();" + "newMap.put(key.toString(), " + "Boolean.valueOf" + "(((org.infinispan.remoting.responses.Response)map2.get(key))" + ".isSuccessful()));}" + MapReduceTaskMonitor.class.getName() + ".postProcessTaskForInvokeRemotely(this, newMap);" + "$_ = map2;" + "}"); } if (className.equals("org.infinispan.commands.read.MapReduceCommand") && methodName.equals("perform")) { m.replace( "{" + "$_ = $proceed($$);" + MapReduceTaskMonitor.class.getName() + ".postProcessTask(this);}"); } } }); }
/** * タスク終了時の処理を埋め込む。 * * @param ctMethod 埋め込み対象のメソッド * @throws CannotCompileException */ private void insertPostProcessTaskNG(final CtBehavior ctMethod) throws CannotCompileException { ctMethod.instrument( new ExprEditor() { public void edit(MethodCall m) throws CannotCompileException { if (m.getClassName().equals("org.infinispan.remoting.rpc.RpcManager") && m.getMethodName().equals("invokeRemotelyInFuture") || m.getClassName().equals("org.infinispan.remoting.rpc.RpcManager") && m.getMethodName().equals("invokeRemotely") || m.getClassName() .equals("org.infinispan.distexec.mapreduce.MapReduceTask.MapReduceFuture") && m.getMethodName().equals("get") || m.getClassName().equals("org.infinispan.commands.read.MapReduceCommand") && m.getMethodName().equals("perform")) { m.replace( "try{$_ = $proceed($$);}catch(Exception e){" + MapReduceTaskMonitor.class.getName() + ".postProcessNGTask(this);}"); } } }); }
/** * Statementのメソッドに対して計測コードを埋め込む。 * * @param pool Statementを含むプール。 * @param ctClass Statementを実装するクラス * @param method メソッド * @param inheritedStatement Statementを実装したクラスを変換する場合は <code>true</code> * @param inheritedPreparedStatement PreparedStatementを実装したクラスを変換する場合は <code>true</code> */ public static void convertStatement( final ClassPool pool, final CtClass ctClass, final CtBehavior method, boolean inheritedStatement, boolean inheritedPreparedStatement) { try { // BCI対象クラス「java.sql.Connection」に対して、コード転換を行う String methodName = method.getName(); // BCI対象クラス「java.sql.Statement」に対して、コード転換を行う if (inheritedStatement) { if (SystemLogger.getInstance().isDebugEnabled()) { SystemLogger.getInstance().debug("JDBC JAVELIN:-->Running ctMethodName:" + methodName); } if ("execute".equals(methodName) || "executeQuery".equals(methodName) || "executeUpdate".equals(methodName) || "executeBatch".equals(methodName)) { convertStatementMethod(pool, ctClass, method); } else if ("addBatch".equals(methodName)) { addSqlToFieldStat(ctClass, method); } else if ("clearBatch".equals(methodName)) { if (inheritedPreparedStatement == false) { delSqlFromField(ctClass, method); } } } // BCI対象クラス「java.sql.PreparedStatement」に対して、コード転換を行う if (inheritedPreparedStatement) { if (SystemLogger.getInstance().isDebugEnabled()) { SystemLogger.getInstance().debug("JDBC JAVELIN:-->Running ctMethodName:" + methodName); } if ("setString".equals(methodName) || "setObject".equals(methodName)) { // パターンAのsetter. 文字列長制限ラベルに具体的な設定値を入れる. long jdbcStringLimitLength = JdbcJavelinRecorder.getConfig().getJdbcStringLimitLength(); String code = BCI_METHOD_A.replaceAll("BCI_METHOD_A_LENGTH", String.valueOf(jdbcStringLimitLength)); convertPreparedMethod(ctClass, method, code, BCI_METHOD_PLANFORPREPARED_SETXXX); } else if ("setBigDecimal".equals(methodName) || "setDate".equals(methodName) || "setTime".equals(methodName) || "setTimestamp".equals(methodName)) { // パターンBのsetter convertPreparedMethod(ctClass, method, BCI_METHOD_B, BCI_METHOD_PLANFORPREPARED_SETXXX); } else if ("setBoolean".equals(methodName) || "setShort".equals(methodName) || "setInt".equals(methodName) || "setLong".equals(methodName) || "setFloat".equals(methodName) || "setDouble".equals(methodName)) { // パターンCのsetter convertPreparedMethod(ctClass, method, BCI_METHOD_C, BCI_METHOD_PLANFORPREPARED_SETXXX); } else if ("setByte".equals(methodName) || "setBytes".equals(methodName)) { // パターンDのsetter convertPreparedMethod(ctClass, method, BCI_METHOD_D, BCI_METHOD_PLANFORPREPARED_SETXXX); } else if ("setArray".equals(methodName) || "setBlob".equals(methodName) || "setClob".equals(methodName) || "setRef".equals(methodName) || "setURL".equals(methodName)) { // パターンEのsetter convertPreparedMethod(ctClass, method, BCI_METHOD_E, BCI_METHOD_PLANFORPREPARED_SETXXX); } else if ("setNull".equals(methodName)) { // パターンFのsetter convertPreparedMethod(ctClass, method, BCI_METHOD_F, BCI_METHOD_PLANFORPREPARED_SETXXX); } else if ("setAsciiStream".equals(methodName) || "setBinaryStream".equals(methodName) || "setUnicodeStream".equals(methodName)) { // TODO Java6.0 でインターフェースの仕様が変わったため、引数の数が3以外のものも対応が必要。 if (method.getParameterTypes().length == ARGS) { // パターンGのsetter convertPreparedMethod( ctClass, method, BCI_METHOD_E, BCI_METHOD_PLANFORPREPARED_SETINPUTSTREAM); } } else if ("setCharacterStream".equals(methodName)) { // TODO Java6.0 でインターフェースの仕様が変わったため、引数の数が3以外のものも対応が必要。 if (method.getParameterTypes().length == ARGS) { // パターンHのsetter convertPreparedMethod( ctClass, method, BCI_METHOD_E, BCI_METHOD_PLANFORPREPARED_SETREADER); } } else if ("addBatch".equals(methodName)) { convertPreparedMethodAddBatch(ctClass, method); } else if ("clearBatch".equals(methodName)) { convertPreparedMethodClearBatch(ctClass, method); } else if ("close".equals(methodName)) { convertPreparedMethodClose(ctClass, method); } if ("execute".equals(methodName) || "executeBatch".equals(methodName) || "executeQuery".equals(methodName) || "executeUpdate".equals(methodName)) { convertExecuteMethod(ctClass, method); } } } catch (Throwable ex) { SystemLogger.getInstance().warn(ex); } }
/** * Statementのメソッドに対して計測コードを埋め込む。 * * @param pool Statementを含むプール。 * @param ctClass Statementを実装するクラス * @return 変換結果 */ public static CtClass convertConnection(final ClassPool pool, final CtClass ctClass) { CtClass jvnConnction; try { jvnConnction = pool.get(JdbcJavelinConnection.class.getCanonicalName()); boolean hasInterface = hasInterface(ctClass, jvnConnction); if (hasInterface == false) { ctClass.addInterface(jvnConnction); } // すでにメソッドを追加している場合は処理を行わない if (hasBehavior(ctClass, "getJdbcJavelinProcessor")) { return ctClass; } CtField procField = CtField.make( "private " + DBProcessor.class.getCanonicalName() + " dbProcessor_;", ctClass); ctClass.addField(procField); CtMethod procGetMethod = CtMethod.make( "public " + DBProcessor.class.getCanonicalName() + " getJdbcJavelinProcessor(){ return dbProcessor_; }", ctClass); ctClass.addMethod(procGetMethod); CtMethod procSetMethod = CtMethod.make( "public void setJdbcJavelinProcessor(" + DBProcessor.class.getCanonicalName() + " dbProcessor){ dbProcessor_ = dbProcessor; }", ctClass); ctClass.addMethod(procSetMethod); CtField urlField = CtField.make("private String jdbcUrl_;", ctClass); ctClass.addField(urlField); CtMethod urlMethod = CtMethod.make("public String getJdbcUrl(){ return jdbcUrl_; }", ctClass); ctClass.addMethod(urlMethod); CtMethod urlSetMethod = CtMethod.make( "public void " + "setJdbcUrl(String jdbcUrl)" + "{ jdbcUrl_ = jdbcUrl; }", ctClass); ctClass.addMethod(urlSetMethod); } catch (NotFoundException ex) { SystemLogger.getInstance().warn(ex); return null; } catch (CannotCompileException ex) { SystemLogger.getInstance().warn(ex); return null; } CtBehavior[] behaviors = ctClass.getDeclaredBehaviors(); for (int index = 0; index < behaviors.length; index++) { CtBehavior method = behaviors[index]; // メソッドの定義がない場合、あるいはpublicでない // (->インターフェースに定義されていない)場合は実行しない。 final int MODIFIER = method.getModifiers(); if (Modifier.isAbstract(MODIFIER) || !Modifier.isPublic(MODIFIER)) { continue; } // BCI対象クラス「java.sql.Connection」に対して、コード転換を行う String methodName = method.getName(); try { if ("prepareStatement".equals(methodName)) { addSqlToFieldCon(ctClass, method); } else if ("prepareCall".equals(methodName)) { addSqlToFieldCon(ctClass, method); } } catch (CannotCompileException ex) { SystemLogger.getInstance().warn(ex); } } try { return ctClass; } catch (Exception ex) { SystemLogger.getInstance().warn(ex); return null; } }