From 482b18e3df057c95c205129b773210a5bc651120 Mon Sep 17 00:00:00 2001 From: denz Date: Tue, 13 Oct 2015 17:43:15 +0900 Subject: [PATCH 1/4] Working realtime chart UI. Working Preference feature. Modify scatter full screen mode bug. - do not work dragging --- .../common/services/navbar-vo.service.js | 10 +- .../common/services/preference.service.js | 57 ++++ .../common/services/server-map-dao.service.js | 2 +- .../features/navbar/navbar.directive.js | 6 +- .../realtime-chart.controller.js | 275 +++++++++++------- .../realtimeChart/realtime-chart.directive.js | 228 ++++++++------- web/src/main/webapp/index.html | 1 + web/src/main/webapp/pages/main/main.html | 93 +----- .../scatter-full-screen-mode.controller.js | 8 +- web/src/main/webapp/styles/main.css | 101 +++++++ 10 files changed, 468 insertions(+), 313 deletions(-) create mode 100644 web/src/main/webapp/common/services/preference.service.js diff --git a/web/src/main/webapp/common/services/navbar-vo.service.js b/web/src/main/webapp/common/services/navbar-vo.service.js index 822927b4d..ed7406b39 100644 --- a/web/src/main/webapp/common/services/navbar-vo.service.js +++ b/web/src/main/webapp/common/services/navbar-vo.service.js @@ -7,7 +7,7 @@ * @name NavbarVoService * @class */ - pinpointApp.factory('NavbarVoService', function () { + pinpointApp.factory('NavbarVoService', [ 'PreferenceService', function (preferenceService) { return function () { // define and initialize private variables; var self = this; @@ -22,9 +22,9 @@ this._sReadablePeriod = false; this._sQueryEndDateTime = false; - - this._nCallerRange = 2; - this._nCalleeRange = 2; + + this._nCallerRange = preferenceService.getDepth(); + this._nCalleeRange = preferenceService.getDepth(); this._sHint = false; @@ -206,5 +206,5 @@ return self; }; }; - }); + }]); })(); \ No newline at end of file diff --git a/web/src/main/webapp/common/services/preference.service.js b/web/src/main/webapp/common/services/preference.service.js new file mode 100644 index 000000000..92f368aae --- /dev/null +++ b/web/src/main/webapp/common/services/preference.service.js @@ -0,0 +1,57 @@ +(function() { + 'use strict'; + /** + * (en)PreferenceService + * @ko PreferenceService + * @group Service + * @name PreferenceService + * @class + */ + pinpointApp.constant('PreferenceServiceConfig', { + name : { + depth: "preference.depth" + }, + DEFAULT_DEPTH: 1 + }); + + pinpointApp.service('PreferenceService', [ 'PreferenceServiceConfig', function(cfg) { + var oDefault = {}; + var bAddedFavorite = false; + var aFavoriteApplicatName = []; + + loadPreference(); + + + this.setDepth = function( d ) { + localStorage.setItem(cfg.name.depth, d); + oDefault.depth = d; + } + this.getDepth = function() { + // @TODO + return oDefault.depth; + }; + this.setUsedApplicationName = function( applicationName ) { + bAdded = true; + var oFavoriate = JSON.parse( localStorage.getItem("favoriate") || "{}" ); + if ( angular.isDefined( oFavoriate[applicationName] ) ) { + oFavoriate[applicationName] += 1; + } else { + oFavoriate[applicationName] = 1; + } + localStorage.setItem("favoriate", JSON.string(oFavoriate) ); + }; + this.getFavoriteApplicationName = function() { + if ( bAddedFavorite ) { + // 반환 값 계산 ( 상위 5개 추리기 ) + } + return aFavoriteApplicationName; + + }; + + function loadPreference() { + oDefault.depth = parseInt( localStorage.getItem( cfg.name.depth ) || cfg.DEFAULT_DEPTH ); + //oDefault.favoriate = JSON.parse( localStorage.getItem("favoriate") || "{}" ); + }; + + }]); +})(); \ No newline at end of file diff --git a/web/src/main/webapp/common/services/server-map-dao.service.js b/web/src/main/webapp/common/services/server-map-dao.service.js index eca1a744b..02aca9637 100644 --- a/web/src/main/webapp/common/services/server-map-dao.service.js +++ b/web/src/main/webapp/common/services/server-map-dao.service.js @@ -18,7 +18,7 @@ FILTER_FETCH_LIMIT: 5000 }); - pinpointApp.service('ServerMapDaoService', [ 'serverMapDaoServiceConfig', function ServerMapDao(cfg) { + pinpointApp.service('ServerMapDaoService', [ 'serverMapDaoServiceConfig', 'PreferenceService', function ServerMapDao(cfg, preferenceService) { var self = this; diff --git a/web/src/main/webapp/features/navbar/navbar.directive.js b/web/src/main/webapp/features/navbar/navbar.directive.js index 30eadf918..4d613e821 100644 --- a/web/src/main/webapp/features/navbar/navbar.directive.js +++ b/web/src/main/webapp/features/navbar/navbar.directive.js @@ -13,15 +13,15 @@ periodTypePrefix: '.navbar.periodType' }); - pinpointApp.directive('navbarDirective', [ 'cfg', '$rootScope', '$http','$document', '$timeout', '$window', 'webStorage', 'helpContentTemplate', 'helpContentService', 'AnalyticsService', - function (cfg, $rootScope, $http, $document, $timeout, $window, webStorage, helpContentTemplate, helpContentService, analyticsService) { + pinpointApp.directive('navbarDirective', [ 'cfg', '$rootScope', '$http','$document', '$timeout', '$window', 'webStorage', 'helpContentTemplate', 'helpContentService', 'AnalyticsService', 'PreferenceService', + function (cfg, $rootScope, $http, $document, $timeout, $window, webStorage, helpContentTemplate, helpContentService, analyticsService, preferenceService) { return { restrict: 'EA', replace: true, templateUrl: 'features/navbar/navbar.html', link: function (scope, element) { - var DEFAULT_RANGE = 2; + var DEFAULT_RANGE = preferenceService.getDepth(); var MAX_RANGE = 8; // define private variables var $application, $fromPicker, $toPicker, oNavbarVoService, aReadablePeriodList; diff --git a/web/src/main/webapp/features/realtimeChart/realtime-chart.controller.js b/web/src/main/webapp/features/realtimeChart/realtime-chart.controller.js index d9ff94b6d..40eabdf97 100644 --- a/web/src/main/webapp/features/realtimeChart/realtime-chart.controller.js +++ b/web/src/main/webapp/features/realtimeChart/realtime-chart.controller.js @@ -9,29 +9,31 @@ */ pinpointApp.constant('RealtimeChartCtrlConfig', { wsUrl: "/agent/activeThread.pinpointws", - agentChartTemplate: '
' + agentChartTemplate: '
', + chartDirectiveTemplate: Handlebars.compile( '' ) }); - pinpointApp.controller('RealtimeChartCtrl', ['RealtimeChartCtrlConfig', '$scope', '$element', '$rootScope', '$compile', 'globalConfig', - function (cfg, $scope, $element, $rootScope, $compile, globalConfig) { + pinpointApp.controller('RealtimeChartCtrl', ['RealtimeChartCtrlConfig', '$scope', '$element', '$rootScope', '$compile', '$window', 'globalConfig', + function (cfg, $scope, $element, $rootScope, $compile, $window, globalConfig) { - $scope.useRealTime = globalConfig.useRealTime || true; - $scope.showRealtime = false; - $scope.currentApplicationName = ""; - $scope.agentCount = 0; - $scope.sumChartColor = ["rgba(44, 160, 44, 1)", "rgba(60, 129, 250, 1)", "rgba(248, 199, 49, 1)", "rgba(246, 145, 36, 1)" ]; - $scope.agentChartColor = ["rgba(44, 160, 44, 0.5)", "rgba(60, 129, 250, 0.5)", "rgba(248, 199, 49, 0.5)", "rgba(246, 145, 36, 0.5)" ]; - var DEFAULT_Y_MAX = 50; - var aSumChartData = getInitChartData(10); - var $elementSumChartWrapper = $element.find("div.agent-sum-chart"); - var $elementAgentChartList = $element.find("div.agent-chart-list"); - var aAgentChartList = []; - var oAgentChartNamespace = {}; - - var wsOpened = false; - var wsocket = null; + var X_AXIS_COUNT = 10; var RECEIVE_SUCCESS = 0; + var $elSumChartWrapper = $element.find("div.agent-sum-chart"); + var $elAgentChartListWrapper = $element.find("div.agent-chart-list"); + var bWebsocketOpened = false; + var websocket = null; + var aSumChartData = [0]; + var aAgentChartElementList = []; + var oNamespaceToIndexMap = {}; + var screenState = "small"; + + $scope.showRealtimeChart = false; + $scope.sumChartColor = ["rgba(44, 160, 44, 1)", "rgba(60, 129, 250, 1)", "rgba(248, 199, 49, 1)", "rgba(246, 145, 36, 1)" ]; + $scope.agentChartColor = ["rgba(44, 160, 44, .8)", "rgba(60, 129, 250, .8)", "rgba(248, 199, 49, .8)", "rgba(246, 145, 36, .8)"]; + $scope.requestLabelNames= [ "Fast", "Normal", "Slow", "Very Slow"]; + $scope.currentAgentCount = 0; + $scope.currentApplicationName = ""; function getInitChartData( len ) { var a = []; @@ -41,121 +43,157 @@ return a; } function initChartDirective() { - var el = $compile('')($scope); - $elementSumChartWrapper.append( el ); + $elSumChartWrapper.append( $compile( cfg.chartDirectiveTemplate({ + "chartColor": "sumChartColor", + "xAxisCount": X_AXIS_COUNT, + "namespace": "sum", + "showExtraInfo": "true", + "height": 120, + "width": 260 + }))($scope) ); + } + function hasAgentChart( agentName ) { + return angular.isDefined( oNamespaceToIndexMap[agentName] ); } function addAgentChart( agentName ) { - var $newAgentChart = $( cfg.agentChartTemplate ).find("div").html(agentName).end(); - var el = $compile('')($scope); + var $newAgentChart = $( cfg.agentChartTemplate ).append( $compile( cfg.chartDirectiveTemplate({ + "chartColor": "agentChartColor", + "xAxisCount": X_AXIS_COUNT, + "namespace": aAgentChartElementList.length, + "showExtraInfo": "false", + "height": 60, + "width": 120 + }))($scope) ); + $elAgentChartListWrapper.append( $newAgentChart ); - $newAgentChart.append( el ); - $elementAgentChartList.append( $newAgentChart ); - - aAgentChartList.push( $newAgentChart ); - oAgentChartNamespace[agentName] = aAgentChartList.length - 1; + linkNamespaceToIndex( agentName, aAgentChartElementList.length ); + aAgentChartElementList.push( $newAgentChart ); } function initWS() { - wsocket = null; - wsocket = new WebSocket("ws://" + location.host + cfg.wsUrl); - wsocket.onopen = function(event) { - wsOpened = true; - console.log( "ws open : ", event ); - send(); - }; - wsocket.onmessage = function(event) { - var data = JSON.parse( event.data ); - receive( data ); - }; - wsocket.onclose = function(event) { - // retry connection - wsOpened = false; - wsocket = null; - console.log( "ws close :", event ); - }; - - initChartDirective(); + websocket = null; + if ( angular.isDefined( WebSocket ) ) { + websocket = new WebSocket("ws://" + location.host + cfg.wsUrl); + websocket.onopen = function(event) { + console.log( "onOpen websocket", event); + bWebsocketOpened = true; + send(); + }; + websocket.onmessage = function(event) { + receive( JSON.parse( event.data ) ); + }; + websocket.onclose = function(event) { + console.log( "onClose websocket", event); + bWebsocketOpened = false; + websocket = null; +// @TODO +// if ( $scope.showRealtimeChart === true ) { +// //reinit +// } + }; + initChartDirective(); + } } function receive( data ) { if ( angular.isUndefined( data[$scope.currentApplicationName] ) ) return; var applicationData = data[$scope.currentApplicationName]; - //@Test-Code -// for( var i = 0 ; i < 7 ; i++ ) { -// applicationData["Naver-agent-" + i] = { -// code: 0, -// message: "OK", -// status: [0, 0, 0, 0], -// timeStamp: Date.now() -// }; -// } - var agentCount = 0; - var aRequestSum = [0, 0, 0, 0]; - var timeStamp; - - for( var p in applicationData ) { - if ( applicationData[p].code === RECEIVE_SUCCESS ) { - var aRequestCount = applicationData[p].status; - for( var i = 0 ; i < aRequestCount.length ; i++ ) { - aRequestSum[i] += aRequestCount[i]; - } - timeStamp = applicationData[p].timeStamp; - } - } - for (var i = 0 ; i < aSumChartData.length ; i++ ) { - aSumChartData[i].push( aRequestSum[i] ); - aSumChartData[i].shift(); - } - var sumOfMaxY = sumOfMax( aSumChartData ); - - for( var p in applicationData ) { - if ( aAgentChartList.length <= agentCount ) { - addAgentChart(p); - } - aAgentChartList[agentCount].show(); - - if ( applicationData[p].code === RECEIVE_SUCCESS ) { - $rootScope.$broadcast('realtimeChartDirective.onData.' + oAgentChartNamespace[p], applicationData[p].status, applicationData[p].timeStamp, sumOfMaxY ); - } else { - //show message - } - agentCount++; - } + var aRequestSum = getSumOfRequestType( applicationData ); + addSumYValue( aRequestSum ); - $rootScope.$broadcast('realtimeChartDirective.onData.sum', aRequestSum, timeStamp, sumOfMaxY ); - $scope.$apply(function() { - $scope.agentCount = agentCount; + broadcastData( applicationData, aRequestSum ); + + } + function broadcastData( applicationData, aRequestSum ) { + var maxY = getMaxOfYValue(); + var agentIndexAndCount = 0; + var timeStamp; + + for( var agentName in applicationData ) { + checkAgentChart( agentName, agentIndexAndCount ); + + timeStamp = applicationData[agentName].timeStamp; + if ( applicationData[agentName].code === RECEIVE_SUCCESS ) { + $rootScope.$broadcast('realtimeChartDirective.onData.' + oNamespaceToIndexMap[agentName], applicationData[agentName].status, timeStamp, maxY ); + } else { + $rootScope.$broadcast('realtimeChartDirective.onError.' + oNamespaceToIndexMap[agentName], applicationData[agentName].message, timeStamp, maxY ); + } + + showAgentChart( agentIndexAndCount ); + agentIndexAndCount++; + } + $rootScope.$broadcast('realtimeChartDirective.onData.sum', aRequestSum, timeStamp, maxY ); + + $scope.$apply(function() { + $scope.currentAgentCount = agentIndexAndCount; }); } - function sumOfMax(datum) { - var sum = 0; - for (var i = 0 ; i < datum.length ; i++ ) { - sum += Math.ceil( d3.max( datum[i], function( d ) { - return d; - }) ); - } - return sum === 0 ? DEFAULT_Y_MAX : sum; + function checkAgentChart( agentName, agentIndexAndCount ) { + if ( hasAgentChart( agentName ) == false ) { + if ( hasNotUseChart( agentIndexAndCount ) ) { + linkNamespaceToIndex(agentName, agentIndexAndCount); + } else { + addAgentChart(agentName); + } + } + setAgentName( agentIndexAndCount, agentName ); + } + function linkNamespaceToIndex( name, index ) { + oNamespaceToIndexMap[name] = index; + } + function hasNotUseChart( index ) { + return aAgentChartElementList.length > index; + } + function showAgentChart( index ) { + aAgentChartElementList[index].show(); + } + function setAgentName( index, name ) { + aAgentChartElementList[index].find("div").html(name); + } + function getSumOfRequestType( datum ) { + var aRequestSum = [0, 0, 0, 0]; + for( var p in datum ) { + if ( datum[p].code === RECEIVE_SUCCESS ) { + jQuery.each(datum[p].status, function( i, v ) { + aRequestSum[i] += v; + }); + } + } + return aRequestSum; + } + function addSumYValue( data ) { + aSumChartData.push( data.reduce(function(pre, cur) { + return pre + cur; + })); + if ( aSumChartData.legnth > X_AXIS_COUNT ) { + aSumChartData.shift(); + } + } + function getMaxOfYValue() { + return d3.max( aSumChartData, function( d ) { + return d; + }); } function send() { - wsocket.send("applicationName=" + $scope.currentApplicationName); + websocket.send("applicationName=" + $scope.currentApplicationName); } function startWS( applicationName ) { $scope.currentApplicationName = applicationName; - if ( wsOpened === false || wsocket == null) { + if ( bWebsocketOpened === false || websocket == null) { initWS(); } else { send(); } $scope.$apply(function() { - $scope.showRealtime = true; + $scope.showRealtimeChart = true; }); } function stopWS() { - $scope.showRealtime = false; - wsocket.send("applicationName="); + $scope.showRealtimeChart = false; + websocket.send("applicationName="); } function stopChart() { $rootScope.$broadcast('realtimeChartDirective.clear.sum'); - $.each( aAgentChartList, function(index, el) { + $.each( aAgentChartElementList, function(index, el) { $rootScope.$broadcast('realtimeChartDirective.clear.' + index); el.hide(); }); @@ -163,19 +201,44 @@ } $scope.$on('realtimeChartController.initialize', function (event, isWas, applicationName) { - if ( $scope.useRealTime === false ) return; + if ( globalConfig.useRealTime === false ) return; + if ( isWas === false && $scope.showRealtimeChart == false ) return; + if ( isWas === true ) { + if ( $scope.showRealtimeChart === true ) { + $scope.closePopup(); + } startWS( applicationName ); } else { stopWS(); } }); + $scope.resizePopup = function() { + switch( screenState ) { + case "full": + $element.css({ + "height": "180px", + "bottom": "184px" + }); + $elAgentChartListWrapper.css("height", "150px"); + screenState = "small"; + break; + case "small": + $element.css({ + "height": ($window.innerHeight - 70) + "px", + "bottom": ($window.innerHeight - 70 + 4) + "px" + }); + $elAgentChartListWrapper.css("height", ($window.innerHeight - 70 - 30) + "px"); + screenState = "full"; + break; + } + } $scope.closePopup = function() { stopWS(); stopChart(); $scope.currentApplicationName = ""; - $scope.agentCount = 0; + $scope.currentAgentCount = 0; } } ]); diff --git a/web/src/main/webapp/features/realtimeChart/realtime-chart.directive.js b/web/src/main/webapp/features/realtimeChart/realtime-chart.directive.js index 9244497a5..55b4d9567 100644 --- a/web/src/main/webapp/features/realtimeChart/realtime-chart.directive.js +++ b/web/src/main/webapp/features/realtimeChart/realtime-chart.directive.js @@ -8,6 +8,7 @@ * @class */ pinpointApp.constant('realtimeChartDirectiveConfig', { + }); pinpointApp.directive('realtimeChartDirective', [ 'realtimeChartDirectiveConfig', '$location', @@ -17,31 +18,34 @@ replace: true, template: '', link: function postLink(scope, element, attrs) { - var aChartColor = scope[attrs["chartColor"]]; - var useLabel = attrs["useLabel"] === "true"; + var aRequestLabel = scope[attrs["requestLabel"]]; + var aRequestColor = scope[attrs["chartColor"]]; + var xAxisCount = parseInt( attrs["xcount"] ); var namespace = attrs["namespace"]; - var svgWidth = parseInt( attrs["width"] ); var svgHeight = parseInt( attrs["height"] ); + var svgWidth = parseInt( attrs["width"] ); + var showExtraInfo = attrs["showExtraInfo"] === "true"; - var svg, svgX, svgY, yAxis, path, area, vLine, labels, tooltip, tooltipDate; - var x_column = 10; - var DEFAULT_Y_MAX = 50; - var options = { - labels: [ "Fast", "Normal", "Slow", "Very Slow"], - domain: [1, x_column - 2], + var svg, svgX, svgY, yAxis, path, area, vLine, labels, tooltip, tooltipDate, errorLabel; + var d3Options = { + domain: [1, xAxisCount - 2], interpolation: "basis", - margin: useLabel ? {top: 6, right: 80, bottom: 6, left: 30} : {top: 6, right: 80, bottom: 6, left: 20} + margin: { + left: showExtraInfo ? 38 : 20, + right: 80, + top: 6, + bottom: 6 + } }; - var latelyXPosition = -1; - var latelyIndex = -1; - var xAxisLength = options.domain[1]; - var aInnerStack = options.datum || getInitData(); + var lastPosition = -1; + var lastIndex = -1; + var aInnerStack = getInitData(); var aRealTimeData = []; var transition = d3.select({}).transition().duration(1000).ease("linear"); - var width = svgWidth - ( useLabel ? options.margin.left : 0 ) - ( useLabel ? options.margin.right : 0 ); - var height = svgHeight - options.margin.top - options.margin.bottom; - var sumOfMaxY = DEFAULT_Y_MAX; -// var sumOfMaxY = sumOfMax(aInnerStack); + var width = svgWidth - ( showExtraInfo ? d3Options.margin.left : 0 ) - ( showExtraInfo ? d3Options.margin.right : 0 ); + var height = svgHeight - d3Options.margin.top - d3Options.margin.bottom; + var sumOfMaxY = 0; + var yAxisFormat = d3.format("d"); var stack = d3.layout.stack().y(function(d) { return d.y; }); stack(aInnerStack); @@ -51,58 +55,59 @@ resetPath(); resetTooltipLine(); resetLabels(); + resetErrorLabel(); + function initGraph() { svg = d3.select( element.get(0) ) - .attr("width", svgWidth) - .attr("height", svgHeight) + .attr({ + "width": svgWidth, + "height": svgHeight + }) .append("g") - .attr("transform", "translate(" + (useLabel ? options.margin.left : 0) + "," + options.margin.top + ")"); + .attr({ + "class": "base", + "transform": "translate(" + (showExtraInfo ? d3Options.margin.left : 0) + "," + d3Options.margin.top + ")" + }); - svg.append("defs").append("clipPath") + svg.append("defs") + .append("clipPath") .attr("id", "clip") .append("rect") - .attr("width", width) - .attr("height", height); + .attr({ + "x": 1, + "y": 0, + "width": width, + "height": height + }); } function getInitData() { var a = []; - for( var i = 0 ; i < options.labels.length ; i++ ) { - a.push( d3.range(xAxisLength + 2).map(function() { return { y: 0 }; }) ); + var now = Date.now(); + for( var i = 0 ; i < aRequestLabel.length ; i++ ) { + a.push( d3.range(xAxisCount).map(function() { return { y: 0, d: now }; }) ); } return a; } -// function resetSumOfMax() { -// sumOfMaxY = parseInt( sumOfMax(aInnerStack) ) + 10; -// } -// function sumOfMax(datum) { -// var sum = 0; -// for (var i = 0 ; i < datum.length ; i++ ) { -// sum += Math.ceil( d3.max( datum[i], function( d ) { -// return d.y; -// }) ); -// } -// return sum === 0 ? DEFAULT_Y_MAX : sum; -// } function resetAxis() { - svgX = d3.scale.linear().domain(options.domain).range([0, width]); + svgX = d3.scale.linear().domain(d3Options.domain).range([0, width]); svgY = d3.scale.linear().domain([0, sumOfMaxY]).range([height, 0]); - if ( useLabel ) { - yAxis = d3.svg.axis().scale(svgY).ticks(3).orient("left"); + if ( showExtraInfo ) { + yAxis = d3.svg.axis().scale(svgY).ticks(3).orient("left").tickFormat(yAxisFormat); svg.append("g").attr("class", "y axis"); } resetYAxis(); } function resetYAxis() { svgY = d3.scale.linear().domain([0, sumOfMaxY]).range([height, 0]); - if ( useLabel ) { + if ( showExtraInfo ) { yAxis = d3.svg.axis().scale(svgY).ticks(3).orient("left"); svg.selectAll("g.y.axis").call(yAxis); } } function resetArea() { area = d3.svg.area() - .interpolate(options.interpolation) + .interpolate(d3Options.interpolation) .x(function(d, i) { return svgX(i); }) .y0(function(d, i) { return svgY(d.y0); }) .y1(function(d, i) { return svgY(d.y + d.y0); }); @@ -115,9 +120,11 @@ .data(aInnerStack) .enter() .append("path") - .attr("class", "area ") - .attr("fill", function(d, i) { return aChartColor[i]; }) - .attr("d", area); + .attr({ + "class": "area ", + "fill": function(d, i) { return aRequestColor[i]; }, + "d": area + }); } function redrawPath() { path.attr("d", area) @@ -125,46 +132,44 @@ .transition() .attr("transform", "translate(" + svgX(0) + ")"); } -// function clearPath() { -// path.attr("d", area).attr("transform", null); -// } function resetTooltipLine() { - if ( useLabel === false ) return; - var vLineOutPosition = -(options.margin.left + 100); + if ( showExtraInfo === false ) return; + var vLineOutPosition = -(d3Options.margin.left + 100); svg .on("mouseout", function() { var position = d3.mouse(this); vLine - .transition() - .attr("x1", vLineOutPosition) + .attr("x1", vLineOutPosition) .attr("x2", vLineOutPosition); tooltip .attr("transform", "translate(" + vLineOutPosition + ", " + vLineOutPosition + ")"); tooltipDate.text(""); - latelyXPosition = -1; - latelyIndex = -1; + lastPosition = -1; + lastIndex = -1; }) .on("mousemove", function(d) { - latelyIndex = parseInt( svgX.invert( d3.mouse(this)[0] ) ); - latelyXPosition = parseInt( d3.mouse(this)[0] ); - latelyXPosition = latelyXPosition > width ? vLineOutPosition : latelyXPosition; + lastIndex = parseInt( svgX.invert( d3.mouse(this)[0] ) ); + lastPosition = parseInt( d3.mouse(this)[0] ); + lastPosition = lastPosition > width ? vLineOutPosition : lastPosition; vLine .transition() .delay(0) .duration(0) - .attr("x1", latelyXPosition) - .attr("x2", latelyXPosition); - resetTooltipLabel( getPositionData( latelyIndex ) ); + .attr("x1", lastPosition) + .attr("x2", lastPosition); + resetTooltipLabel( getPositionData( lastIndex ) ); }); vLine = svg .append("line") - .attr("class", "guideLine") - .attr("x1", 0) - .attr("y1", 10) - .attr("x2", 0) - .attr("y2", height); + .attr({ + "class": "guideLine", + "x1": 0, + "y1": 10, + "x2": 0, + "y2": height + }); tooltip = svg .append("g") @@ -173,34 +178,39 @@ }); tooltip .append("rect") - .attr("width", 20) - .attr("height", 90) - .attr("fill", "#000"); + .attr({ + "width": 40, + "height": 90, + "fill": "#000", + "fill-opacity": "0.7" + }); tooltip .selectAll("text") - .data( options.labels ) + .data( aRequestLabel ) .enter() .append("text") .attr("x", function(d, i) { - return 6; + return 37; }) .attr("y", function(d, i) { - return ((aChartColor.length - i - 1) * 20 + 20) + "px"; + return ((aRequestColor.length - i - 1) * 20 + 20) + "px"; }) .attr("fill", function(d, i) { - return aChartColor[i]; + return aRequestColor[i]; }) .style("font-size", "12px") .text(function(d, i) { - return options.labels[i]; + return aRequestLabel[i]; }); tooltipDate = svg.append("text") - .attr("x", "29%") - .attr("y", "6px") - .attr("fill", "#000") - .attr("text-anchor", "middle") - .attr("font-size", "14px") + .attr({ + "x": "29%", + "y": "6px", + "fill": "#000", + "text-anchor": "middle", + "font-size": "14px" + }) .text(""); } function getPositionData( index ) { @@ -209,50 +219,61 @@ for( var i = 0 ; i < aInnerStack.length ; i++ ) { a.push( { y: aInnerStack[i][index] ? aInnerStack[i][index].y : 0, - d: aInnerStack[i][index].d + d: aInnerStack[i][index] ? aInnerStack[i][index].d : Date.now() }); } return a; } function resetTooltipLabel( datum ) { - if ( useLabel === false ) return; - if ( latelyXPosition === -1 || latelyIndex === -1 ) return; + if ( showExtraInfo === false ) return; + if ( lastPosition === -1 || lastIndex === -1 ) return; tooltip - .attr("transform", "translate(" + (latelyXPosition - 30) + ", 10)") + .attr("transform", "translate(" + (lastPosition - 44) + ", 10)") .selectAll("text") + .attr("text-anchor", "end") .text(function(d, i) { return datum[i].y; }); tooltipDate.text( d3.time.format("%Y.%m.%d %H:%M:%S")(new Date(datum[0].d)) ); } function resetLabels( datum ) { - if ( useLabel === false ) return; + if ( showExtraInfo === false ) return; labels = svg.append("g") .attr("transform", function(d, i) { - return "translate(" + (svgWidth - options.margin.left - options.margin.right + 4) + ",10)"; + return "translate(" + (svgWidth - d3Options.margin.left - d3Options.margin.right + 4) + ",10)"; }) .selectAll("text") - .data( options.labels ) + .data( aRequestLabel ) .enter() .append("text") .attr("y", function(d, i) { - return ( (options.labels.length - i - 1) / options.labels.length) * 100 + "%"; + return ( (aRequestLabel.length - i - 1) / aRequestLabel.length) * 100 + "%"; }) .attr("fill", function(d, i) { - return aChartColor[i]; + return aRequestColor[i]; }) .attr("font-size", "12px") .attr("font-weight", "bold") .text(function(d, i) { - return options.labels[i]; + return aRequestLabel[i]; }); } + function resetErrorLabel() { + errorLabel = svg.append("text") + .attr({ + "y": "40%", + "x": "50%", + "font-weight": "bold", + "text-anchor": "middle", + "fill": "#F00" + }); + } function resetLabelData( datum ) { - if ( useLabel === false ) return; + if ( showExtraInfo === false ) return; labels .data( datum ) .text(function(d, i) { - return typeof d.y !== "undefined" ? ( d.y + " : " + options.labels[i] ) : options.labels[i]; + return typeof d.y !== "undefined" ? ( d.y + " : " + aRequestLabel[i] ) : aRequestLabel[i]; }); } function tick() { @@ -261,7 +282,6 @@ var aNewData = aRealTimeData.shift(); resetLabelData( aNewData ); -// resetSumOfMax(); resetYAxis(); var i = 0; @@ -273,7 +293,7 @@ for( i = 0 ; i < aInnerStack.length ; i++ ) { aInnerStack[i].shift(); } - resetTooltipLabel( getPositionData( latelyIndex ) ); + resetTooltipLabel( getPositionData( lastIndex ) ); }).transition().each("start", function() { tick(); }); @@ -282,20 +302,18 @@ scope.$on('realtimeChartDirective.onData.' + namespace, function (event, aNewRequestCount, timeStamp, maxY) { sumOfMaxY = maxY; - - aRealTimeData.push( (function() { - var a = []; - for (var i = 0 ; i < aNewRequestCount.length ; i++ ) { - a.push({ -// @TestCode - y: parseInt(aNewRequestCount[i] + (Math.random() * 10)), -// y: parseInt( aNewRequestCount[i]), - d: timeStamp - }); + errorLabel.text(""); + aRealTimeData.push( aNewRequestCount.map(function(v, i) { + return { + y: parseInt( v ), + d: timeStamp } - return a; - })() ); + }) ); }); + scope.$on('realtimeChartDirective.onError.' + namespace, function (event, errorMessage, timeStamp, maxY) { + sumOfMaxY = maxY; + errorLabel.text( errorMessage ); + }); scope.$on('realtimeChartDirective.clear.' + namespace, function (event, aNewRequestCount, timeStamp) { aRealTimeData.length = 0; aInnerStack.length = 0; diff --git a/web/src/main/webapp/index.html b/web/src/main/webapp/index.html index 6a43e0d1b..9c16c85a1 100644 --- a/web/src/main/webapp/index.html +++ b/web/src/main/webapp/index.html @@ -418,6 +418,7 @@ + diff --git a/web/src/main/webapp/pages/main/main.html b/web/src/main/webapp/pages/main/main.html index 0986f7cf8..f6f8da005 100644 --- a/web/src/main/webapp/pages/main/main.html +++ b/web/src/main/webapp/pages/main/main.html @@ -5,101 +5,14 @@
-
- - +
Realtime Active Thread Chart +
-
{{currentApplicationName}} ({{agentCount == 0 ? "..." : agentCount}})
+
{{currentApplicationName}} ({{currentAgentCount == 0 ? "..." : currentAgentCount}})
diff --git a/web/src/main/webapp/pages/scatterFullScreenMode/scatter-full-screen-mode.controller.js b/web/src/main/webapp/pages/scatterFullScreenMode/scatter-full-screen-mode.controller.js index f034bb61b..e9cce6082 100644 --- a/web/src/main/webapp/pages/scatterFullScreenMode/scatter-full-screen-mode.controller.js +++ b/web/src/main/webapp/pages/scatterFullScreenMode/scatter-full-screen-mode.controller.js @@ -7,10 +7,12 @@ * @name ScatterFullScreenModeCtrl * @class */ - pinpointApp.controller('ScatterFullScreenModeCtrl', [ '$scope', '$rootScope', '$routeParams', '$timeout', 'NavbarVoService', 'AnalyticsService', - function ($scope, $rootScope, $routeParams, $timeout, NavbarVoService, analyticsService) { + pinpointApp.controller('ScatterFullScreenModeCtrl', [ '$scope', '$rootScope', '$window', '$routeParams', '$timeout', 'NavbarVoService', 'AnalyticsService', + function ($scope, $rootScope, $window, $routeParams, $timeout, NavbarVoService, analyticsService) { analyticsService.send(analyticsService.CONST.SCATTER_FULL_SCREEN_PAGE); - // define private variables + $window.htoScatter = $window.htoScatter || {}; + $window.$routeParams = $window.$routeParams || $routeParams; + // define private variables var oNavbarVoService; // initialize diff --git a/web/src/main/webapp/styles/main.css b/web/src/main/webapp/styles/main.css index e853cf821..5e41660c0 100644 --- a/web/src/main/webapp/styles/main.css +++ b/web/src/main/webapp/styles/main.css @@ -233,4 +233,105 @@ canvas {outline:none} padding-left: 20px; } +.realtime { + left: 1%; + width: 98%; + height: 180px; + border: 2px solid #64a71a; + bottom: 184px; + z-index: 10; + position: relative; + border-radius: 6px; + background-color: #F0F3F4; +} +.realtime .chart-title { + color: white; + width: 100%; + padding: 2px 0px 2px 6px; + position: absolute; + border-radius: 3px 3px 0px 0px; + background-color: #7ED321; +} +.realtime .chart-close { + float: right; + cursor: pointer; + margin-right: 6px; +} +.realtime .chart-expand { + float: right; + cursor: pointer; + margin-right: 6px; +} +.realtime .agent-sum-chart { + top: 28px; + left: 0px; + width: 280px; + cursor: crosshair; + position: relative; + text-align: center; + background-color: #FFF; +} +.realtime .agent-sum-chart div { + font-weight: bold; + margin-bottom: 4px; +} +.realtime .agent-chart-list { + top: -119px; + height: 150px; + padding: 2px 4px 2px 4px; + position: relative; + overflow-y: auto; + margin-left: 280px; + margin-right: 6px; +} +.realtime .agent-chart { + float: left; + width: 120px; + height: 70px; + margin: 1px 2px 1px 4px; + box-shadow: 1px 1px 3px 0px rgba(0,0,0,0.75); + border-radius: 4px 4px 0px 0px; + background-color: #FFF; +} +.realtime .agent-chart > div { + color: #000; + padding: 1px; + overflow: hidden; + font-size: 10px; + word-wrap: normal; + text-align: center; + font-family: Times; + white-space: nowrap; + text-overflow: ellipsis; + border-radius: 4px 4px 0px 0px; + background-color: rgba(126, 211, 33, 0.5); +} +.realtime .agent-sum-chart svg { + font: 10px sans-serif; + display: block; +} +.realtime .agent-chart-list svg { + font: 8px sans-serif; + display: block; +} +.realtime .agent-sum-chart .axis path, .axis line, .agent-chart-list .axis path, .axis line { + fill: none; + stroke: #000; + shape-rendering: crispEdges; +} +.realtime .agent-sum-chart .guideLine, .agent-chart-list .guideLine { + fill: none; + stroke: #000; + shape-rendering: crispEdges; +} +.realtime .agent-sum-chart .x.axis area, .agent-chart-list .x.axis area { + shape-rendering: auto; +} +.realtime .agent-sum-chart .area, .agent-chart-list .area { + stroke: none; + stroke-width: 1.5px; +} +.realtime .agent-chart-list text { + font-size: 8px; +} From f7b325ee3b26d9ffb0542066936fb7e7b26289a7 Mon Sep 17 00:00:00 2001 From: "koo.taejin" Date: Tue, 13 Oct 2015 17:44:55 +0900 Subject: [PATCH 2/4] Implement realtime span delivery service. #560 --- .../collector/cluster/ClusterPointRouter.java | 3 +- .../cluster/route/StreamRouteHandler.java | 24 ++- .../pinpoint/profiler/DefaultAgent.java | 1 + .../profiler/receiver/CommandDispatcher.java | 35 +--- .../profiler/receiver/CommandSerializer.java | 54 ++++++ .../ProfilerCommandServiceRegistry.java | 6 +- .../ProfilerStreamCommandService.java | 3 +- .../service/ActiveThreadCountService.java | 118 +++++++++++- .../rpc/packet/stream/StreamCode.java | 2 +- ...treamChannelMessageListenerRepository.java | 105 ++++++++++ .../LoggingStreamChannelMessageListener.java | 2 +- .../rpc/stream/StreamChannelManager.java | 25 ++- .../StreamChannelStateChangeEventHandler.java | 6 +- .../rpc/stream/StreamChannelManagerTest.java | 2 +- .../pinpoint/web/service/AgentService.java | 6 + .../web/service/AgentServiceImpl.java | 30 ++- .../web/vo/AgentActiveThreadCount.java | 54 ++++-- .../web/vo/AgentActiveThreadCountList.java | 10 +- .../websocket/ActiveThreadCountHandler.java | 180 ++++++++---------- .../ActiveThreadCountStreamListener.java | 153 +++++++++++++++ .../WebSocketResponseAggregator.java | 165 ++++++++++++++++ .../vo/AgentActiveThreadCountListTest.java | 13 +- 22 files changed, 798 insertions(+), 199 deletions(-) create mode 100644 profiler/src/main/java/com/navercorp/pinpoint/profiler/receiver/CommandSerializer.java create mode 100644 rpc/src/main/java/com/navercorp/pinpoint/rpc/stream/ClientStreamChannelMessageListenerRepository.java create mode 100644 web/src/main/java/com/navercorp/pinpoint/web/websocket/ActiveThreadCountStreamListener.java create mode 100644 web/src/main/java/com/navercorp/pinpoint/web/websocket/WebSocketResponseAggregator.java diff --git a/collector/src/main/java/com/navercorp/pinpoint/collector/cluster/ClusterPointRouter.java b/collector/src/main/java/com/navercorp/pinpoint/collector/cluster/ClusterPointRouter.java index e6e624377..4d6d4863f 100644 --- a/collector/src/main/java/com/navercorp/pinpoint/collector/cluster/ClusterPointRouter.java +++ b/collector/src/main/java/com/navercorp/pinpoint/collector/cluster/ClusterPointRouter.java @@ -101,7 +101,6 @@ public class ClusterPointRouter implements MessageListener, ServerStreamChannelM } else { handleRouteRequestFail("Unknown error.", requestPacket, pinpointSocket); } - } @Override @@ -157,7 +156,7 @@ public class ClusterPointRouter implements MessageListener, ServerStreamChannelM return StreamCode.ROUTE_ERROR; } - return StreamCode.SUCCESS; + return StreamCode.OK; } public ClusterPointRepository getTargetClusterPointRepository() { diff --git a/collector/src/main/java/com/navercorp/pinpoint/collector/cluster/route/StreamRouteHandler.java b/collector/src/main/java/com/navercorp/pinpoint/collector/cluster/route/StreamRouteHandler.java index 531a59783..213afe234 100644 --- a/collector/src/main/java/com/navercorp/pinpoint/collector/cluster/route/StreamRouteHandler.java +++ b/collector/src/main/java/com/navercorp/pinpoint/collector/cluster/route/StreamRouteHandler.java @@ -22,6 +22,7 @@ import com.navercorp.pinpoint.collector.cluster.TargetClusterPoint; import com.navercorp.pinpoint.collector.cluster.route.filter.RouteFilter; import com.navercorp.pinpoint.rpc.ResponseMessage; import com.navercorp.pinpoint.rpc.packet.stream.StreamClosePacket; +import com.navercorp.pinpoint.rpc.packet.stream.StreamCode; import com.navercorp.pinpoint.rpc.packet.stream.StreamResponsePacket; import com.navercorp.pinpoint.rpc.server.PinpointServer; import com.navercorp.pinpoint.rpc.stream.*; @@ -111,6 +112,7 @@ public class StreamRouteHandler extends AbstractRouteHandler { ClientStreamChannelContext producerContext = createStreamChannel((PinpointServerClusterPoint) clusterPoint, event.getDeliveryCommand().getPayload(), routeManager); if (producerContext.getCreateFailPacket() == null) { routeManager.setProducer(producerContext.getStreamChannel()); + producerContext.getStreamChannel().addStateChangeEventHandler(routeManager); return createResponse(TRouteResult.OK); } } else { @@ -139,7 +141,7 @@ public class StreamRouteHandler extends AbstractRouteHandler { } // fix me : StreamRouteManager will change worker thread pattern. - private class StreamRouteManager implements ClientStreamChannelMessageListener { + private class StreamRouteManager implements ClientStreamChannelMessageListener,StreamChannelStateChangeEventHandler { private final StreamEvent streamEvent; private final ServerStreamChannel consumer; @@ -187,6 +189,26 @@ public class StreamRouteHandler extends AbstractRouteHandler { } } + @Override + public void eventPerformed(ClientStreamChannel streamChannel, StreamChannelStateCode updatedStateCode) throws Exception { + logger.info("eventPerformed streamChannel:{}, stateCode:{}", streamChannel, updatedStateCode); + + switch (updatedStateCode) { + case CLOSED: + case ILLEGAL_STATE: + if (consumer != null) { + consumer.close(); + } + break; + } + + } + + @Override + public void exceptionCaught(ClientStreamChannel streamChannel, StreamChannelStateCode updatedStateCode, Throwable e) { + logger.warn("exceptionCaught message:{}, streamChannel:{}, stateCode:{}", e.getMessage(), streamChannel, updatedStateCode, e); + } + public ClientStreamChannel getProducer() { return producer; } diff --git a/profiler/src/main/java/com/navercorp/pinpoint/profiler/DefaultAgent.java b/profiler/src/main/java/com/navercorp/pinpoint/profiler/DefaultAgent.java index a05ab6871..b9b29017e 100644 --- a/profiler/src/main/java/com/navercorp/pinpoint/profiler/DefaultAgent.java +++ b/profiler/src/main/java/com/navercorp/pinpoint/profiler/DefaultAgent.java @@ -22,6 +22,7 @@ import java.util.Map; import java.util.Properties; import java.util.Set; +import com.navercorp.pinpoint.profiler.receiver.service.ActiveThreadCountService; import com.navercorp.pinpoint.rpc.client.PinpointClient; import com.navercorp.pinpoint.rpc.util.ClientFactoryUtils; import org.slf4j.Logger; diff --git a/profiler/src/main/java/com/navercorp/pinpoint/profiler/receiver/CommandDispatcher.java b/profiler/src/main/java/com/navercorp/pinpoint/profiler/receiver/CommandDispatcher.java index 2123b1203..28b0f1b55 100644 --- a/profiler/src/main/java/com/navercorp/pinpoint/profiler/receiver/CommandDispatcher.java +++ b/profiler/src/main/java/com/navercorp/pinpoint/profiler/receiver/CommandDispatcher.java @@ -16,7 +16,6 @@ package com.navercorp.pinpoint.profiler.receiver; -import com.navercorp.pinpoint.common.Version; import com.navercorp.pinpoint.rpc.MessageListener; import com.navercorp.pinpoint.rpc.PinpointSocket; import com.navercorp.pinpoint.rpc.packet.RequestPacket; @@ -26,13 +25,10 @@ import com.navercorp.pinpoint.rpc.packet.stream.StreamCode; import com.navercorp.pinpoint.rpc.packet.stream.StreamCreatePacket; import com.navercorp.pinpoint.rpc.stream.ServerStreamChannelContext; import com.navercorp.pinpoint.rpc.stream.ServerStreamChannelMessageListener; -import com.navercorp.pinpoint.rpc.util.AssertUtils; import com.navercorp.pinpoint.thrift.dto.TResult; import com.navercorp.pinpoint.thrift.io.*; import com.navercorp.pinpoint.thrift.util.SerializationUtils; import org.apache.thrift.TBase; -import org.apache.thrift.protocol.TCompactProtocol; -import org.apache.thrift.protocol.TProtocolFactory; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -44,29 +40,8 @@ public class CommandDispatcher implements MessageListener, ServerStreamChannelMe private final Logger logger = LoggerFactory.getLogger(this.getClass()); private final ProfilerCommandServiceRegistry commandServiceRegistry = new ProfilerCommandServiceRegistry(); - - private final SerializerFactory serializerFactory; - private final DeserializerFactory deserializerFactory; public CommandDispatcher() { - this(Version.VERSION); - } - - public CommandDispatcher(String pinpointVersion) { - this(pinpointVersion, HeaderTBaseSerializerFactory.DEFAULT_UDP_STREAM_MAX_SIZE); - } - - public CommandDispatcher(String pinpointVersion, int serializationMaxSize) { - TProtocolFactory protocolFactory = new TCompactProtocol.Factory(); - TCommandRegistry commandTbaseRegistry = new TCommandRegistry(TCommandTypeVersion.getVersion(pinpointVersion)); - - SerializerFactory serializerFactory = new HeaderTBaseSerializerFactory(true, serializationMaxSize, protocolFactory, commandTbaseRegistry); - this.serializerFactory = wrappedThreadLocalSerializerFactory(serializerFactory); - AssertUtils.assertNotNull(this.serializerFactory); - - DeserializerFactory deserializerFactory = new HeaderTBaseDeserializerFactory(protocolFactory, commandTbaseRegistry); - this.deserializerFactory = wrappedThreadLocalDeserializerFactory(deserializerFactory); - AssertUtils.assertNotNull(this.deserializerFactory); } @Override @@ -78,7 +53,7 @@ public class CommandDispatcher implements MessageListener, ServerStreamChannelMe public void handleRequest(RequestPacket requestPacket, PinpointSocket pinpointSocket) { logger.info("handleRequest packet:{}, remote:{}", requestPacket, pinpointSocket.getRemoteAddress()); - final TBase request = SerializationUtils.deserialize(requestPacket.getPayload(), deserializerFactory, null); + final TBase request = SerializationUtils.deserialize(requestPacket.getPayload(), CommandSerializer.DESERIALIZER_FACTORY, null); logger.debug("handleRequest request:{}, remote:{}", request, pinpointSocket.getRemoteAddress()); TBase response; @@ -99,7 +74,7 @@ public class CommandDispatcher implements MessageListener, ServerStreamChannelMe } } - final byte[] payload = SerializationUtils.serialize(response, serializerFactory, null); + final byte[] payload = SerializationUtils.serialize(response, CommandSerializer.SERIALIZER_FACTORY, null); if (payload != null) { pinpointSocket.response(requestPacket, payload); } @@ -109,7 +84,7 @@ public class CommandDispatcher implements MessageListener, ServerStreamChannelMe public StreamCode handleStreamCreate(ServerStreamChannelContext streamChannelContext, StreamCreatePacket packet) { logger.info("MessageReceived handleStreamCreate {} {}", packet, streamChannelContext); - final TBase request = SerializationUtils.deserialize(packet.getPayload(), deserializerFactory, null); + final TBase request = SerializationUtils.deserialize(packet.getPayload(), CommandSerializer.DESERIALIZER_FACTORY, null); if (request == null) { return StreamCode.TYPE_UNKNOWN; } @@ -119,9 +94,7 @@ public class CommandDispatcher implements MessageListener, ServerStreamChannelMe return StreamCode.TYPE_UNSUPPORT; } - service.streamCommandService(request, streamChannelContext); - - return StreamCode.SUCCESS; + return service.streamCommandService(request, streamChannelContext); } @Override diff --git a/profiler/src/main/java/com/navercorp/pinpoint/profiler/receiver/CommandSerializer.java b/profiler/src/main/java/com/navercorp/pinpoint/profiler/receiver/CommandSerializer.java new file mode 100644 index 000000000..c1ae2e203 --- /dev/null +++ b/profiler/src/main/java/com/navercorp/pinpoint/profiler/receiver/CommandSerializer.java @@ -0,0 +1,54 @@ +/* + * + * * 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.profiler.receiver; + +import com.navercorp.pinpoint.common.Version; +import com.navercorp.pinpoint.thrift.io.*; +import org.apache.thrift.protocol.TCompactProtocol; +import org.apache.thrift.protocol.TProtocolFactory; + +/** + * @Author Taejin Koo + */ +public class CommandSerializer { + + public static final SerializerFactory SERIALIZER_FACTORY; + public static final DeserializerFactory DESERIALIZER_FACTORY; + + static { + TProtocolFactory protocolFactory = new TCompactProtocol.Factory(); + TCommandRegistry commandTbaseRegistry = new TCommandRegistry(TCommandTypeVersion.getVersion(Version.VERSION)); + + SerializerFactory serializerFactory = new HeaderTBaseSerializerFactory(true, HeaderTBaseSerializerFactory.DEFAULT_UDP_STREAM_MAX_SIZE, protocolFactory, commandTbaseRegistry); + SERIALIZER_FACTORY = wrappedThreadLocalSerializerFactory(serializerFactory); + + DeserializerFactory deserializerFactory = new HeaderTBaseDeserializerFactory(protocolFactory, commandTbaseRegistry); + DESERIALIZER_FACTORY = wrappedThreadLocalDeserializerFactory(deserializerFactory); + } + + private static SerializerFactory wrappedThreadLocalSerializerFactory(SerializerFactory serializerFactory) { + return new ThreadLocalHeaderTBaseSerializerFactory(serializerFactory); + } + + private static DeserializerFactory wrappedThreadLocalDeserializerFactory(DeserializerFactory deserializerFactory) { + return new ThreadLocalHeaderTBaseDeserializerFactory(deserializerFactory); + } + +} diff --git a/profiler/src/main/java/com/navercorp/pinpoint/profiler/receiver/ProfilerCommandServiceRegistry.java b/profiler/src/main/java/com/navercorp/pinpoint/profiler/receiver/ProfilerCommandServiceRegistry.java index df8171861..baa54afea 100644 --- a/profiler/src/main/java/com/navercorp/pinpoint/profiler/receiver/ProfilerCommandServiceRegistry.java +++ b/profiler/src/main/java/com/navercorp/pinpoint/profiler/receiver/ProfilerCommandServiceRegistry.java @@ -84,7 +84,7 @@ public class ProfilerCommandServiceRegistry implements ProfilerCommandServiceLoc } final ProfilerCommandService service = profilerCommandServiceRepository.get(tBase.getClass()); - if (service instanceof ProfilerSimpleCommandService) { + if (service != null && (service instanceof ProfilerSimpleCommandService)) { return (ProfilerSimpleCommandService) service; } @@ -98,7 +98,7 @@ public class ProfilerCommandServiceRegistry implements ProfilerCommandServiceLoc } final ProfilerCommandService service = profilerCommandServiceRepository.get(tBase.getClass()); - if (service instanceof ProfilerRequestCommandService) { + if (service != null && (service instanceof ProfilerRequestCommandService)) { return (ProfilerRequestCommandService) service; } @@ -112,7 +112,7 @@ public class ProfilerCommandServiceRegistry implements ProfilerCommandServiceLoc } final ProfilerCommandService service = profilerCommandServiceRepository.get(tBase.getClass()); - if (service instanceof ProfilerStreamCommandService) { + if (service != null && (service instanceof ProfilerStreamCommandService)) { return (ProfilerStreamCommandService) service; } diff --git a/profiler/src/main/java/com/navercorp/pinpoint/profiler/receiver/ProfilerStreamCommandService.java b/profiler/src/main/java/com/navercorp/pinpoint/profiler/receiver/ProfilerStreamCommandService.java index f0e0fce06..8247adea8 100644 --- a/profiler/src/main/java/com/navercorp/pinpoint/profiler/receiver/ProfilerStreamCommandService.java +++ b/profiler/src/main/java/com/navercorp/pinpoint/profiler/receiver/ProfilerStreamCommandService.java @@ -16,12 +16,13 @@ package com.navercorp.pinpoint.profiler.receiver; +import com.navercorp.pinpoint.rpc.packet.stream.StreamCode; import org.apache.thrift.TBase; import com.navercorp.pinpoint.rpc.stream.ServerStreamChannelContext; public interface ProfilerStreamCommandService extends ProfilerCommandService { - short streamCommandService(TBase tBase, ServerStreamChannelContext streamChannelContext); + StreamCode streamCommandService(TBase tBase, ServerStreamChannelContext streamChannelContext); } diff --git a/profiler/src/main/java/com/navercorp/pinpoint/profiler/receiver/service/ActiveThreadCountService.java b/profiler/src/main/java/com/navercorp/pinpoint/profiler/receiver/service/ActiveThreadCountService.java index 8ea0fa5ca..313ebad3a 100644 --- a/profiler/src/main/java/com/navercorp/pinpoint/profiler/receiver/service/ActiveThreadCountService.java +++ b/profiler/src/main/java/com/navercorp/pinpoint/profiler/receiver/service/ActiveThreadCountService.java @@ -24,17 +24,47 @@ import com.navercorp.pinpoint.common.trace.HistogramSlot; import com.navercorp.pinpoint.common.trace.SlotType; import com.navercorp.pinpoint.profiler.context.active.ActiveTraceInfo; import com.navercorp.pinpoint.profiler.context.active.ActiveTraceLocator; +import com.navercorp.pinpoint.profiler.receiver.CommandSerializer; import com.navercorp.pinpoint.profiler.receiver.ProfilerRequestCommandService; +import com.navercorp.pinpoint.profiler.receiver.ProfilerStreamCommandService; +import com.navercorp.pinpoint.rpc.packet.stream.StreamCode; +import com.navercorp.pinpoint.rpc.stream.*; +import com.navercorp.pinpoint.rpc.util.TimerFactory; import com.navercorp.pinpoint.thrift.dto.command.TCmdActiveThreadCount; import com.navercorp.pinpoint.thrift.dto.command.TCmdActiveThreadCountRes; +import com.navercorp.pinpoint.thrift.util.SerializationUtils; import org.apache.thrift.TBase; +import org.jboss.netty.util.HashedWheelTimer; +import org.jboss.netty.util.Timeout; +import org.jboss.netty.util.TimerTask; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; -import java.util.*; +import java.util.ArrayList; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.concurrent.CopyOnWriteArrayList; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicBoolean; /** * @author Taejin Koo */ -public class ActiveThreadCountService implements ProfilerRequestCommandService { +public class ActiveThreadCountService implements ProfilerRequestCommandService, ProfilerStreamCommandService { + + private final Logger logger = LoggerFactory.getLogger(this.getClass()); + + private final Object lock = new Object(); + + // it will be changed. + private final StreamChannelStateChangeEventHandler stateChangeEventHandler = new ActiveThreadCountStreamChannelStateChangeEventHandler(); + private final HashedWheelTimer timer = TimerFactory.createHashedWheelTimer("ActiveThreadCountService-Timer", 100, TimeUnit.MILLISECONDS, 512); + private final long time = 1000; + private final AtomicBoolean onTimerTask = new AtomicBoolean(false); + + private final List streamChannelRepository = new CopyOnWriteArrayList(); + private static final List ACTIVE_THREAD_SLOTS_ORDER = new ArrayList(); static { @@ -57,8 +87,29 @@ public class ActiveThreadCountService implements ProfilerRequestCommandService { this.activeThreadSlotsCount = ACTIVE_THREAD_SLOTS_ORDER.size(); } + @Override - public TBase requestCommandService(TBase tBase) { + public Class getCommandClazz() { + return TCmdActiveThreadCount.class; + } + + @Override + public TBase requestCommandService(TBase activeThreadCountObject) { + if (activeThreadCountObject == null) { + throw new NullPointerException("activeThreadCountObject may not be null."); + } + + return getActiveThreadCountResponse(); + } + + @Override + public StreamCode streamCommandService(TBase tBase, ServerStreamChannelContext streamChannelContext) { + logger.info("streamCommandService object:{}, streamChannelContext:{}", tBase, streamChannelContext); + streamChannelContext.getStreamChannel().addStateChangeEventHandler(stateChangeEventHandler); + return StreamCode.OK; + } + + private TCmdActiveThreadCountRes getActiveThreadCountResponse() { Map mappedSlot = new LinkedHashMap(activeThreadSlotsCount); for (SlotType slotType : ACTIVE_THREAD_SLOTS_ORDER) { mappedSlot.put(slotType, new IntAdder(0)); @@ -85,10 +136,6 @@ public class ActiveThreadCountService implements ProfilerRequestCommandService { return response; } - @Override - public Class getCommandClazz() { - return TCmdActiveThreadCount.class; - } private static class IntAdder { private int value = 0; @@ -106,4 +153,61 @@ public class ActiveThreadCountService implements ProfilerRequestCommandService { } } + private class ActiveThreadCountStreamChannelStateChangeEventHandler implements StreamChannelStateChangeEventHandler { + + private final LoggingStreamChannelStateChangeEventHandler loggingStateChangeEventListener = new LoggingStreamChannelStateChangeEventHandler(); + + @Override + public void eventPerformed(ServerStreamChannel streamChannel, StreamChannelStateCode updatedStateCode) throws Exception { + synchronized (lock) { + switch (updatedStateCode) { + case CONNECTED: + streamChannelRepository.add(streamChannel); + boolean turnOn = onTimerTask.compareAndSet(false, true); + if (turnOn) { + timer.newTimeout(new ActiveThreadCountTimerTask(), time, TimeUnit.MILLISECONDS); + } + break; + case CLOSED: + case ILLEGAL_STATE: + boolean removed = streamChannelRepository.remove(streamChannel); + if (removed) { + if (streamChannelRepository.size() == 0) { + boolean turnOff = onTimerTask.compareAndSet(true, false); + } + } + break; + } + } + } + + @Override + public void exceptionCaught(ServerStreamChannel streamChannel, StreamChannelStateCode updatedStateCode, Throwable e) { + } + + } + + private class ActiveThreadCountTimerTask implements TimerTask { + + @Override + public void run(Timeout timeout) throws Exception { + logger.info("ActiveThreadCountService timer started."); + + try { + TCmdActiveThreadCountRes activeThreadCountResponse = getActiveThreadCountResponse(); + for (ServerStreamChannel serverStreamChannel : streamChannelRepository) { + byte[] payload = SerializationUtils.serialize(activeThreadCountResponse, CommandSerializer.SERIALIZER_FACTORY, null); + if (payload != null) { + serverStreamChannel.sendData(payload); + } + } + } finally { + if (timer != null && onTimerTask.get()) { + timer.newTimeout(this, time, TimeUnit.MILLISECONDS); + } + } + } + + } + } diff --git a/rpc/src/main/java/com/navercorp/pinpoint/rpc/packet/stream/StreamCode.java b/rpc/src/main/java/com/navercorp/pinpoint/rpc/packet/stream/StreamCode.java index 678482dfd..5b64d8289 100644 --- a/rpc/src/main/java/com/navercorp/pinpoint/rpc/packet/stream/StreamCode.java +++ b/rpc/src/main/java/com/navercorp/pinpoint/rpc/packet/stream/StreamCode.java @@ -29,7 +29,7 @@ import java.util.Map; public enum StreamCode { // Status Code - SUCCESS((short) 0), + OK((short) 0), UNKNWON_ERROR((short) 100), diff --git a/rpc/src/main/java/com/navercorp/pinpoint/rpc/stream/ClientStreamChannelMessageListenerRepository.java b/rpc/src/main/java/com/navercorp/pinpoint/rpc/stream/ClientStreamChannelMessageListenerRepository.java new file mode 100644 index 000000000..3445c0396 --- /dev/null +++ b/rpc/src/main/java/com/navercorp/pinpoint/rpc/stream/ClientStreamChannelMessageListenerRepository.java @@ -0,0 +1,105 @@ +/* + * + * * 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.rpc.stream; + +import com.navercorp.pinpoint.rpc.util.StringUtils; + +import java.util.Collection; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +/** + * @Author Taejin Koo + */ +public class ClientStreamChannelMessageListenerRepository { + + private final ConcurrentHashMap repository = new ConcurrentHashMap(); + + public void put(String key, V messageListener) { + if (StringUtils.isEmpty(key)) { + throw new IllegalArgumentException("key is empty."); + } + + if (messageListener == null) { + throw new IllegalArgumentException("messageListener is null."); + } + + repository.put(key, messageListener); + } + + public void get(String key) { + repository.get(key); + } + + public void remove(String key) { + if (StringUtils.isEmpty(key)) { + return; + } + + repository.remove(key); + } + + public void remove(V messageListener) { + if (messageListener == null) { + return; + } + + String key = getKey(messageListener); + repository.remove(key); + } + + public boolean contains(String key) { + if (StringUtils.isEmpty(key)) { + return false; + } + + return repository.containsKey(key); + } + + public boolean contains(V value) { + if (value == null) { + return false; + } + + return repository.contains(value); + } + + private String getKey(V messageListener) { + for (Map.Entry entry : repository.entrySet()) { + String key = entry.getKey(); + V value = entry.getValue(); + + if (messageListener == value) { + return key; + } + } + + return null; + } + + public Collection values() { + return repository.values(); + } + + public int size() { + return repository.size(); + } + +} diff --git a/rpc/src/main/java/com/navercorp/pinpoint/rpc/stream/LoggingStreamChannelMessageListener.java b/rpc/src/main/java/com/navercorp/pinpoint/rpc/stream/LoggingStreamChannelMessageListener.java index 9a1d20b07..bee01d05c 100644 --- a/rpc/src/main/java/com/navercorp/pinpoint/rpc/stream/LoggingStreamChannelMessageListener.java +++ b/rpc/src/main/java/com/navercorp/pinpoint/rpc/stream/LoggingStreamChannelMessageListener.java @@ -38,7 +38,7 @@ public class LoggingStreamChannelMessageListener { @Override public StreamCode handleStreamCreate(ServerStreamChannelContext streamChannelContext, StreamCreatePacket packet) { LOGGER.info("handleStreamCreate StreamChannel:{}, Packet:{}", streamChannelContext, packet); - return StreamCode.SUCCESS; + return StreamCode.OK; } @Override diff --git a/rpc/src/main/java/com/navercorp/pinpoint/rpc/stream/StreamChannelManager.java b/rpc/src/main/java/com/navercorp/pinpoint/rpc/stream/StreamChannelManager.java index abef1073b..8404d97dd 100644 --- a/rpc/src/main/java/com/navercorp/pinpoint/rpc/stream/StreamChannelManager.java +++ b/rpc/src/main/java/com/navercorp/pinpoint/rpc/stream/StreamChannelManager.java @@ -16,20 +16,19 @@ package com.navercorp.pinpoint.rpc.stream; -import java.util.Set; -import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.ConcurrentMap; - +import com.navercorp.pinpoint.rpc.PinpointSocketException; +import com.navercorp.pinpoint.rpc.packet.PacketType; import com.navercorp.pinpoint.rpc.packet.stream.*; +import com.navercorp.pinpoint.rpc.util.AssertUtils; +import com.navercorp.pinpoint.rpc.util.IDGenerator; import org.jboss.netty.channel.Channel; import org.jboss.netty.channel.ChannelFuture; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import com.navercorp.pinpoint.rpc.PinpointSocketException; -import com.navercorp.pinpoint.rpc.packet.PacketType; -import com.navercorp.pinpoint.rpc.util.AssertUtils; -import com.navercorp.pinpoint.rpc.util.IDGenerator; +import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentMap; /** * @author koo.taejin @@ -178,22 +177,22 @@ public class StreamChannelManager { private void handleCreate(StreamCreatePacket packet) { final int streamChannelId = packet.getStreamChannelId(); - StreamCode code = StreamCode.SUCCESS; + StreamCode code = StreamCode.OK; ServerStreamChannel streamChannel = new ServerStreamChannel(this.channel, streamChannelId, this); ServerStreamChannelContext streamChannelContext = new ServerStreamChannelContext(streamChannel); code = registerStreamChannel(streamChannelContext); - if (code == StreamCode.SUCCESS) { + if (code == StreamCode.OK) { code = streamChannelMessageListener.handleStreamCreate(streamChannelContext, (StreamCreatePacket) packet); - if (code == StreamCode.SUCCESS) { + if (code == StreamCode.OK) { streamChannel.changeStateConnected(); streamChannel.sendCreateSuccess(); } } - if (code != StreamCode.SUCCESS) { + if (code != StreamCode.OK) { clearResourceAndSendCreateFail(streamChannelId, code); } } @@ -215,7 +214,7 @@ public class StreamChannelManager { return StreamCode.STATE_ERROR; } - return StreamCode.SUCCESS; + return StreamCode.OK; } private void handleCreateSuccess(ClientStreamChannelContext streamChannelContext, StreamCreateSuccessPacket packet) { diff --git a/rpc/src/main/java/com/navercorp/pinpoint/rpc/stream/StreamChannelStateChangeEventHandler.java b/rpc/src/main/java/com/navercorp/pinpoint/rpc/stream/StreamChannelStateChangeEventHandler.java index 0927605b6..cde442266 100644 --- a/rpc/src/main/java/com/navercorp/pinpoint/rpc/stream/StreamChannelStateChangeEventHandler.java +++ b/rpc/src/main/java/com/navercorp/pinpoint/rpc/stream/StreamChannelStateChangeEventHandler.java @@ -22,10 +22,10 @@ package com.navercorp.pinpoint.rpc.stream; /** * @Author Taejin Koo */ -public interface StreamChannelStateChangeEventHandler { +public interface StreamChannelStateChangeEventHandler { - void eventPerformed(StreamChannel streamChannel, StreamChannelStateCode updatedStateCode) throws Exception; + void eventPerformed(S streamChannel, StreamChannelStateCode updatedStateCode) throws Exception; - void exceptionCaught(StreamChannel streamChannel, StreamChannelStateCode updatedStateCode, Throwable e); + void exceptionCaught(S streamChannel, StreamChannelStateCode updatedStateCode, Throwable e); } diff --git a/rpc/src/test/java/com/navercorp/pinpoint/rpc/stream/StreamChannelManagerTest.java b/rpc/src/test/java/com/navercorp/pinpoint/rpc/stream/StreamChannelManagerTest.java index 32828ae86..2a86b4838 100644 --- a/rpc/src/test/java/com/navercorp/pinpoint/rpc/stream/StreamChannelManagerTest.java +++ b/rpc/src/test/java/com/navercorp/pinpoint/rpc/stream/StreamChannelManagerTest.java @@ -335,7 +335,7 @@ public class StreamChannelManagerTest { @Override public StreamCode handleStreamCreate(ServerStreamChannelContext streamChannelContext, StreamCreatePacket packet) { bo.addServerStreamChannelContext(streamChannelContext); - return StreamCode.SUCCESS; + return StreamCode.OK; } @Override diff --git a/web/src/main/java/com/navercorp/pinpoint/web/service/AgentService.java b/web/src/main/java/com/navercorp/pinpoint/web/service/AgentService.java index 10ac5bb9a..5fa69fcdb 100644 --- a/web/src/main/java/com/navercorp/pinpoint/web/service/AgentService.java +++ b/web/src/main/java/com/navercorp/pinpoint/web/service/AgentService.java @@ -56,4 +56,10 @@ public interface AgentService { AgentActiveThreadCountList getActiveThreadCount(List agentInfoList) throws TException; AgentActiveThreadCountList getActiveThreadCount(List agentInfoList, byte[] payload) throws TException; + byte[] serializeRequest(TBase tBase) throws TException; + byte[] serializeRequest(TBase tBase, byte[] defaultValue); + + TBase deserializeResponse(byte[] objectData) throws TException; + TBase deserializeResponse(byte[] objectData, TBase defaultValue); + } diff --git a/web/src/main/java/com/navercorp/pinpoint/web/service/AgentServiceImpl.java b/web/src/main/java/com/navercorp/pinpoint/web/service/AgentServiceImpl.java index fc0749ea4..20a7e77af 100644 --- a/web/src/main/java/com/navercorp/pinpoint/web/service/AgentServiceImpl.java +++ b/web/src/main/java/com/navercorp/pinpoint/web/service/AgentServiceImpl.java @@ -236,8 +236,13 @@ public class AgentServiceImpl implements AgentService { AgentInfo agentInfo = entry.getKey(); PinpointRouteResponse response = entry.getValue(); - AgentActiveThreadCount agentActiveThreadStatus = new AgentActiveThreadCount(agentInfo.getAgentId(), - response.getRouteResult(), response.getResponse(TCmdActiveThreadCountRes.class, null)); + AgentActiveThreadCount agentActiveThreadStatus = new AgentActiveThreadCount(agentInfo.getAgentId()); + TRouteResult routeResult = response.getRouteResult(); + if (routeResult == TRouteResult.OK) { + agentActiveThreadStatus.setResult(response.getResponse(TCmdActiveThreadCountRes.class, null)); + } else { + agentActiveThreadStatus.setFail(routeResult.name()); + } agentActiveThreadStatusList.add(agentActiveThreadStatus); } @@ -285,4 +290,25 @@ public class AgentServiceImpl implements AgentService { return Math.max(startTime + timeout - System.currentTimeMillis(), 100L); } + + @Override + public byte[] serializeRequest(TBase tBase) throws TException { + return SerializationUtils.serialize(tBase, commandSerializerFactory); + } + + @Override + public byte[] serializeRequest(TBase tBase, byte[] defaultValue) { + return SerializationUtils.serialize(tBase, commandSerializerFactory, defaultValue); + } + + @Override + public TBase deserializeResponse(byte[] objectData) throws TException { + return SerializationUtils.deserialize(objectData, commandDeserializerFactory); + } + + @Override + public TBase deserializeResponse(byte[] objectData, TBase defaultValue) { + return SerializationUtils.deserialize(objectData, commandDeserializerFactory, defaultValue); + } + } diff --git a/web/src/main/java/com/navercorp/pinpoint/web/vo/AgentActiveThreadCount.java b/web/src/main/java/com/navercorp/pinpoint/web/vo/AgentActiveThreadCount.java index fd7ccb9b1..850d4adf2 100644 --- a/web/src/main/java/com/navercorp/pinpoint/web/vo/AgentActiveThreadCount.java +++ b/web/src/main/java/com/navercorp/pinpoint/web/vo/AgentActiveThreadCount.java @@ -27,47 +27,59 @@ import com.navercorp.pinpoint.thrift.dto.command.TRouteResult; */ public class AgentActiveThreadCount { - private final String agentId; - private final TRouteResult routeResult; - private final TCmdActiveThreadCountRes activeThreadCount; + private final short OK_CODE = 0; + private final String OK_CODE_MESSAGE = "OK"; - public AgentActiveThreadCount(String agentId, TRouteResult routeResult, TCmdActiveThreadCountRes activeThreadCount) { + private final String agentId; + + private short code = -1; + private String codeMessage = "UNKNOWN"; + + private TCmdActiveThreadCountRes activeThreadCount; + + public AgentActiveThreadCount(String agentId) { this.agentId = agentId; - this.routeResult = routeResult; - this.activeThreadCount = activeThreadCount; + } + + public void setResult(TCmdActiveThreadCountRes activeThreadCount) { + if (activeThreadCount != null) { + this.activeThreadCount = activeThreadCount; + this.code = OK_CODE; + this.codeMessage = OK_CODE_MESSAGE; + } + } + + public void setFail(String codeMessage) { + setFail((short) -1, codeMessage); + } + + public void setFail(short code, String codeMessage) { + this.code = code; + this.codeMessage = codeMessage; } public String getAgentId() { return agentId; } - public TRouteResult getRouteResult() { - return routeResult; + public short getCode() { + return code; } - public TRouteResult getRouteResult(TRouteResult defaultValue) { - if (routeResult == null) { - return defaultValue; - } - return routeResult; + public String getCodeMessage() { + return codeMessage; } public TCmdActiveThreadCountRes getActiveThreadCount() { return activeThreadCount; } - public TCmdActiveThreadCountRes getActiveThreadStatus(TCmdActiveThreadCountRes defaultValue) { - if (activeThreadCount == null) { - return defaultValue; - } - return activeThreadCount; - } - @Override public String toString() { return "AgentActiveThreadCount{" + "agentId='" + agentId + '\'' + - ", routeResult=" + routeResult + + ", code=" + getCode() + + ", codeMessage=" + getCodeMessage() + ", activeThreadCount=" + activeThreadCount + '}'; } diff --git a/web/src/main/java/com/navercorp/pinpoint/web/vo/AgentActiveThreadCountList.java b/web/src/main/java/com/navercorp/pinpoint/web/vo/AgentActiveThreadCountList.java index 9271be706..881a237ec 100644 --- a/web/src/main/java/com/navercorp/pinpoint/web/vo/AgentActiveThreadCountList.java +++ b/web/src/main/java/com/navercorp/pinpoint/web/vo/AgentActiveThreadCountList.java @@ -39,6 +39,10 @@ public class AgentActiveThreadCountList { private final List agentActiveThreadRepository; + public AgentActiveThreadCountList() { + agentActiveThreadRepository = new ArrayList(); + } + public AgentActiveThreadCountList(int initialCapacity) { agentActiveThreadRepository = new ArrayList(initialCapacity); } @@ -72,9 +76,8 @@ class AgentActiveThreadCountListSerializer extends JsonSerializer sessionRepository = new CopyOnWriteArrayList(); + private final Map aggregatorRepository = new HashMap(); + private final ObjectMapper jsonConverter = new ObjectMapper(); public ActiveThreadCountHandler(WebSocketHandlerRegister register, AgentService agentSerivce) { @@ -90,8 +88,6 @@ public class ActiveThreadCountHandler extends TextWebSocketHandler implements Pi synchronized (lock) { sessionRepository.add(newSession); - Timeout timeout = timer.newTimeout(new ActiveThreadTimerTask(), time, TimeUnit.MILLISECONDS); - boolean turnOn = onTimerTask.compareAndSet(false, true); if (turnOn) { timer.newTimeout(new ActiveThreadTimerTask(), time, TimeUnit.MILLISECONDS); @@ -106,6 +102,8 @@ public class ActiveThreadCountHandler extends TextWebSocketHandler implements Pi logger.info("ConnectionClosed : {}, caused : {}", closeSession, status); synchronized (lock) { + closeAggregator(closeSession); + sessionRepository.remove(closeSession); if (sessionRepository.size() == 0) { boolean turnOff = onTimerTask.compareAndSet(true, false); @@ -116,17 +114,70 @@ public class ActiveThreadCountHandler extends TextWebSocketHandler implements Pi } @Override - protected void handleTextMessage(WebSocketSession session, TextMessage message) throws Exception { - logger.info("handleTextMessage. session : {}, message : {}.", session, message.getPayload()); + protected void handleTextMessage(WebSocketSession webSocketSession, TextMessage message) throws Exception { + logger.info("handleTextMessage. session : {}, message : {}.", webSocketSession, message.getPayload()); String request = message.getPayload(); if (request != null && request.startsWith(APPLICATION_NAME_KEY + "=")) { String applicationName = request.substring(APPLICATION_NAME_KEY.length() + 1); - session.getAttributes().put(APPLICATION_NAME_KEY, applicationName); + synchronized (lock) { + closeAggregator(webSocketSession); + if (!StringUtils.isEmpty(applicationName)) { + webSocketSession.getAttributes().put(APPLICATION_NAME_KEY, applicationName); + openAggregator(webSocketSession); + } + } } // this method will be checked socket status. - super.handleTextMessage(session, message); + super.handleTextMessage(webSocketSession, message); + } + + private void openAggregator(WebSocketSession webSocketSession) { + String applicationName = (String) webSocketSession.getAttributes().get(APPLICATION_NAME_KEY); + if (StringUtils.isEmpty(applicationName)) { + return; + } + + WebSocketResponseAggregator aggregator = aggregatorRepository.get(applicationName); + if (aggregator == null) { + aggregator = new WebSocketResponseAggregator(applicationName); + aggregatorRepository.put(applicationName, aggregator); + } + + ClientStreamChannelMessageListenerRepository streamMessageListenerRepository = aggregator.getStreamMessageListenerRepository(); + List agentInfoList = agentSerivce.getAgentInfoList(applicationName); + + for (AgentInfo agentInfo : agentInfoList) { + String agentId = agentInfo.getAgentId(); + if (!streamMessageListenerRepository.contains(agentId)) { + ActiveThreadCountStreamListener streamListener = new ActiveThreadCountStreamListener(agentSerivce, agentInfo, aggregator); + streamListener.start(); + } + } + + aggregator.registerWebSocketSession(webSocketSession); + } + + private void closeAggregator(WebSocketSession webSocketSession) { + String applicationName = (String) webSocketSession.getAttributes().get(APPLICATION_NAME_KEY); + if (StringUtils.isEmpty(applicationName)) { + return; + } + + WebSocketResponseAggregator aggregator = aggregatorRepository.get(applicationName); + if (aggregator == null) { + return; + } + + aggregator.unregisterWebSocketSession(webSocketSession); + if (aggregator.registeredWebSocketSessionCount() == 0) { + for (ActiveThreadCountStreamListener r : aggregator.getStreamMessageListenerRepository().values()) { + r.stop(); + } + + aggregatorRepository.remove(applicationName); + } } private class ActiveThreadTimerTask implements TimerTask { @@ -136,14 +187,13 @@ public class ActiveThreadCountHandler extends TextWebSocketHandler implements Pi try { logger.info("ActiveThreadTimerTask started."); - Map> applicationGroup = createApplicationGroup(sessionRepository); - - for (Map.Entry> applicationEntry : applicationGroup.entrySet()) { - String applicationName = applicationEntry.getKey(); - - List agentInfoList = getAgentInfoList(applicationName); - AgentActiveThreadCountList agentActiveThreadCountList = getAgentActiveThreadCount(agentInfoList); - doResponse(applicationEntry.getValue(), applicationName, agentActiveThreadCountList); + Collection values = aggregatorRepository.values(); + for (WebSocketResponseAggregator aggregator : values) { + try { + aggregator.flush(); + } catch (Exception e) { + logger.warn(e.getMessage(), e); + } } } finally { if (timer != null && onTimerTask.get()) { @@ -153,84 +203,4 @@ public class ActiveThreadCountHandler extends TextWebSocketHandler implements Pi } } - private Map> createApplicationGroup(List sessionRepository) { - Map> applicationGroup = new HashMap>(); - for (WebSocketSession session : sessionRepository) { - String applicationName = (String) session.getAttributes().get(APPLICATION_NAME_KEY); - - if (applicationName == null || applicationName.length() == 0) { - continue; - } - - if (!applicationGroup.containsKey(applicationName)) { - applicationGroup.put(applicationName, new ArrayList()); - } - - applicationGroup.get(applicationName).add(session); - } - - return applicationGroup; - } - - private List getAgentInfoList(String applicationName) { - try { - List agentInfoList = agentSerivce.getAgentInfoList(applicationName); - return agentInfoList; - } catch (Exception e) { - logger.warn(e.getMessage(), e); - } - return Collections.emptyList(); - } - - private AgentActiveThreadCountList getAgentActiveThreadCount(List agentInfoList) { - try { - AgentActiveThreadCountList agentActiveThreadCountList = agentSerivce.getActiveThreadCount(agentInfoList); - return agentActiveThreadCountList; - } catch (TException e) { - logger.warn(e.getMessage(), e); - } - - return new AgentActiveThreadCountList(0); - } - - private void doResponse(List webSocketSessions, String applicationName, AgentActiveThreadCountList activeThreadCount) { - if (webSocketSessions == null) { - return; - } - - String textMessage = makeResponseMessage(applicationName, activeThreadCount); - - for (WebSocketSession session : webSocketSessions) { - try { - session.sendMessage(new TextMessage(textMessage)); - } catch (IOException e) { - logger.warn(e.getMessage(), e); - } - } - } - - private String makeResponseMessage(String applicationName, AgentActiveThreadCountList activeThreadCount) { - Map response = new HashMap(); - response.put(applicationName, activeThreadCount); - - try { - return jsonConverter.writeValueAsString(response); - } catch (JsonProcessingException e) { - logger.warn(e.getMessage(), e); - } - - return createEmptyJsonMessage(applicationName); - } - - private String createEmptyJsonMessage(String applicationName) { - StringBuilder emptyJsonMessage = new StringBuilder(); - emptyJsonMessage.append("{"); - emptyJsonMessage.append("\"").append(applicationName).append("\""); - emptyJsonMessage.append(":"); - emptyJsonMessage.append("{}"); - emptyJsonMessage.append("}"); - - return emptyJsonMessage.toString(); - } - } diff --git a/web/src/main/java/com/navercorp/pinpoint/web/websocket/ActiveThreadCountStreamListener.java b/web/src/main/java/com/navercorp/pinpoint/web/websocket/ActiveThreadCountStreamListener.java new file mode 100644 index 000000000..9a13fb6c1 --- /dev/null +++ b/web/src/main/java/com/navercorp/pinpoint/web/websocket/ActiveThreadCountStreamListener.java @@ -0,0 +1,153 @@ +/* + * + * * 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.web.websocket; + +import com.navercorp.pinpoint.rpc.packet.stream.StreamClosePacket; +import com.navercorp.pinpoint.rpc.packet.stream.StreamCode; +import com.navercorp.pinpoint.rpc.packet.stream.StreamCreateFailPacket; +import com.navercorp.pinpoint.rpc.packet.stream.StreamResponsePacket; +import com.navercorp.pinpoint.rpc.stream.*; +import com.navercorp.pinpoint.thrift.dto.command.TCmdActiveThreadCount; +import com.navercorp.pinpoint.thrift.dto.command.TCmdActiveThreadCountRes; +import com.navercorp.pinpoint.thrift.dto.command.TCommandTransferResponse; +import com.navercorp.pinpoint.thrift.dto.command.TRouteResult; +import com.navercorp.pinpoint.web.service.AgentService; +import com.navercorp.pinpoint.web.vo.AgentActiveThreadCount; +import com.navercorp.pinpoint.web.vo.AgentInfo; +import org.apache.thrift.TBase; +import org.apache.thrift.TException; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * @Author Taejin Koo + */ +public class ActiveThreadCountStreamListener implements ClientStreamChannelMessageListener, StreamChannelStateChangeEventHandler { + + private static final ClientStreamChannelMessageListener LOGGING = LoggingStreamChannelMessageListener.CLIENT_LISTENER; + + private final Logger logger = LoggerFactory.getLogger(this.getClass()); + + private final AgentService agentService; + private final AgentInfo agentInfo; + private final WebSocketResponseAggregator responseAggregator; + private final AgentActiveThreadCount defaultFailedResponse; + private StreamChannel streamchannel; + + public ActiveThreadCountStreamListener(AgentService agentService, AgentInfo agentInfo, WebSocketResponseAggregator webSocketResponseAggregator) { + this.agentService = agentService; + this.agentInfo = agentInfo; + this.responseAggregator = webSocketResponseAggregator; + + this.defaultFailedResponse = new AgentActiveThreadCount(agentInfo.getAgentId()); + } + + public void start() { + try { + ClientStreamChannelContext clientStreamChannelContext = agentService.openStream(agentInfo, new TCmdActiveThreadCount(), this); + + if (clientStreamChannelContext.getCreateFailPacket() == null) { + this.streamchannel = clientStreamChannelContext.getStreamChannel(); + this.streamchannel.addStateChangeEventHandler(this); + defaultFailedResponse.setFail(TRouteResult.TIMEOUT.name()); + } else { + StreamCreateFailPacket createFailPacket = clientStreamChannelContext.getCreateFailPacket(); + defaultFailedResponse.setFail(createFailPacket.getCode().name()); + } + } catch (TException exception) { + defaultFailedResponse.setFail(TRouteResult.NOT_SUPPORTED_REQUEST.name()); + } finally { + this.responseAggregator.getStreamMessageListenerRepository().put(agentInfo.getAgentId(), this); + } + } + + // fixed 결과값을 지정할수 있게 해야함 + public void stop() { + try { + if (streamchannel != null) { + streamchannel.close(); + } + defaultFailedResponse.setFail(StreamCode.STATE_CLOSED.name()); + } finally { + this.responseAggregator.getStreamMessageListenerRepository().remove(agentInfo.getAgentId()); + } + } + + @Override + public void handleStreamData(ClientStreamChannelContext streamChannelContext, StreamResponsePacket packet) { + LOGGING.handleStreamData(streamChannelContext, packet); + + TBase response = agentService.deserializeResponse(packet.getPayload(), null); + AgentActiveThreadCount activeThreadCount = getAgentActiveThreadCount(response); + responseAggregator.response(activeThreadCount); + } + + private AgentActiveThreadCount getAgentActiveThreadCount(TBase routeResponse) { + AgentActiveThreadCount agentActiveThreadCount = new AgentActiveThreadCount(agentInfo.getAgentId()); + + if (routeResponse != null && (routeResponse instanceof TCommandTransferResponse)) { + byte[] payload = ((TCommandTransferResponse) routeResponse).getPayload(); + TBase activeThreadCountResponse = agentService.deserializeResponse(payload, null); + + if (activeThreadCountResponse != null && (activeThreadCountResponse instanceof TCmdActiveThreadCountRes)) { + agentActiveThreadCount.setResult((TCmdActiveThreadCountRes) activeThreadCountResponse); + } else { + agentActiveThreadCount.setFail("ROUTE_ERROR:" + TRouteResult.NOT_SUPPORTED_RESPONSE.name()); + } + } else { + agentActiveThreadCount.setFail("ROUTE_ERROR:" + TRouteResult.BAD_RESPONSE.name()); + } + + return agentActiveThreadCount; + } + + @Override + public void handleStreamClose(ClientStreamChannelContext streamChannelContext, StreamClosePacket packet) { + LOGGING.handleStreamClose(streamChannelContext, packet); + + defaultFailedResponse.setFail(StreamCode.STATE_CLOSED.name()); + } + + @Override + public void eventPerformed(ClientStreamChannel streamChannel, StreamChannelStateCode updatedStateCode) throws Exception { + logger.info("eventPerformed streamChannel:{}, stateCode:{}", streamChannel, updatedStateCode); + + switch (updatedStateCode) { + case CLOSED: + case ILLEGAL_STATE: + defaultFailedResponse.setFail(StreamCode.STATE_CLOSED.name()); + break; + } + } + + @Override + public void exceptionCaught(ClientStreamChannel streamChannel, StreamChannelStateCode updatedStateCode, Throwable e) { + logger.warn("exceptionCaught message:{}, streamChannel:{}, stateCode:{}", e.getMessage(), streamChannel, updatedStateCode, e); + } + + public AgentInfo getAgentInfo() { + return agentInfo; + } + + public AgentActiveThreadCount getDefaultFailedResponse() { + return defaultFailedResponse; + } + +} diff --git a/web/src/main/java/com/navercorp/pinpoint/web/websocket/WebSocketResponseAggregator.java b/web/src/main/java/com/navercorp/pinpoint/web/websocket/WebSocketResponseAggregator.java new file mode 100644 index 000000000..70ca010bc --- /dev/null +++ b/web/src/main/java/com/navercorp/pinpoint/web/websocket/WebSocketResponseAggregator.java @@ -0,0 +1,165 @@ +/* + * + * * 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.web.websocket; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.navercorp.pinpoint.rpc.stream.ClientStreamChannelMessageListenerRepository; +import com.navercorp.pinpoint.web.vo.AgentActiveThreadCount; +import com.navercorp.pinpoint.web.vo.AgentActiveThreadCountList; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.web.socket.TextMessage; +import org.springframework.web.socket.WebSocketSession; + +import java.io.IOException; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.concurrent.CopyOnWriteArrayList; + +/** + * @Author Taejin Koo + */ +public class WebSocketResponseAggregator { + + private final Logger logger = LoggerFactory.getLogger(this.getClass()); + + private final Object lock = new Object(); + private final ObjectMapper jsonConverter = new ObjectMapper(); + + private final String applicationName; + private final List webSocketSessions = new CopyOnWriteArrayList<>(); + private final ClientStreamChannelMessageListenerRepository streamMessageListenerRepository; + + private Map activeThreadCountMap; + + public WebSocketResponseAggregator(String applicationName) { + this.applicationName = applicationName; + this.streamMessageListenerRepository = new ClientStreamChannelMessageListenerRepository(); + + this.activeThreadCountMap = new HashMap(streamMessageListenerRepository.size()); + } + + public void registerWebSocketSession(WebSocketSession webSocketSession) { + if (webSocketSession == null) { + return; + } + + logger.info("registerWebSocketSession webSocketSession:{}"); + synchronized (lock) { + this.webSocketSessions.add(webSocketSession); + } + } + + public void unregisterWebSocketSession(WebSocketSession webSocketSession) { + if (webSocketSession == null) { + return; + } + + logger.info("unregisterWebSocketSession webSocketSession:{}"); + synchronized (lock) { + this.webSocketSessions.remove(webSocketSession); + } + } + + public int registeredWebSocketSessionCount() { + synchronized (lock) { + return this.webSocketSessions.size(); + } + } + + public ClientStreamChannelMessageListenerRepository getStreamMessageListenerRepository() { + return streamMessageListenerRepository; + } + + public void response(AgentActiveThreadCount activeThreadCount) { + if (activeThreadCount == null) { + return; + } + + synchronized (lock) { + this.activeThreadCountMap.put(activeThreadCount.getAgentId(), activeThreadCount); + } + } + + public void flush() throws Exception { + logger.info("flush"); + + AgentActiveThreadCountList response = new AgentActiveThreadCountList(); + synchronized (lock) { + for (ActiveThreadCountStreamListener threadCountStreamListener : streamMessageListenerRepository.values()) { + String agentId = threadCountStreamListener.getAgentInfo().getAgentId(); + + AgentActiveThreadCount agentActiveThreadCount = activeThreadCountMap.get(agentId); + if (agentActiveThreadCount != null) { + response.add(agentActiveThreadCount); + } else { + response.add(threadCountStreamListener.getDefaultFailedResponse()); + } + } + activeThreadCountMap = new HashMap(streamMessageListenerRepository.size()); + } + + flush0(response); + } + + private void flush0(AgentActiveThreadCountList activeThreadCountList) { + String response = makeResponseMessage(applicationName, activeThreadCountList); + + for (WebSocketSession webSocketSession : webSocketSessions) { + try { + logger.debug("flush webSocket:{}, response:{}", webSocketSession, response); + webSocketSession.sendMessage(new TextMessage(response)); + } catch (IOException e) { + logger.warn(e.getMessage(), e); + } + } + + } + + private String makeResponseMessage(String applicationName, AgentActiveThreadCountList activeThreadCount) { + Map response = new HashMap(); + + response.put("applicationName", applicationName); + response.put("activeThreadCounts", activeThreadCount); + response.put("timeStamp", System.currentTimeMillis()); + + try { + return jsonConverter.writeValueAsString(response); + } catch (JsonProcessingException e) { + logger.warn(e.getMessage(), e); + } + + return createEmptyJsonMessage(applicationName); + } + + private String createEmptyJsonMessage(String applicationName) { + StringBuilder emptyJsonMessage = new StringBuilder(); + emptyJsonMessage.append("{"); + emptyJsonMessage.append("\"").append(applicationName).append("\""); + emptyJsonMessage.append(":"); + emptyJsonMessage.append("{}"); + emptyJsonMessage.append("}"); + + return emptyJsonMessage.toString(); + } + +} diff --git a/web/src/test/java/com/navercorp/pinpoint/web/vo/AgentActiveThreadCountListTest.java b/web/src/test/java/com/navercorp/pinpoint/web/vo/AgentActiveThreadCountListTest.java index 552410437..77e708fc0 100644 --- a/web/src/test/java/com/navercorp/pinpoint/web/vo/AgentActiveThreadCountListTest.java +++ b/web/src/test/java/com/navercorp/pinpoint/web/vo/AgentActiveThreadCountListTest.java @@ -40,11 +40,13 @@ public class AgentActiveThreadCountListTest { String hostName1 = "hostName1"; String hostName2 = "hostName2"; - AgentActiveThreadCount status1 = new AgentActiveThreadCount(hostName1, TRouteResult.NOT_ACCEPTABLE, null); + AgentActiveThreadCount status1 = new AgentActiveThreadCount(hostName1); + status1.setFail(TRouteResult.NOT_ACCEPTABLE.name()); TCmdActiveThreadCountRes response = new TCmdActiveThreadCountRes(); response.setActiveThreadCount(Arrays.asList(1, 2, 3, 4)); - AgentActiveThreadCount status2 = new AgentActiveThreadCount(hostName2, TRouteResult.OK, response); + AgentActiveThreadCount status2 = new AgentActiveThreadCount(hostName2); + status2.setResult(response); AgentActiveThreadCountList list = new AgentActiveThreadCountList(5); list.add(status1); @@ -64,7 +66,12 @@ public class AgentActiveThreadCountListTest { } void assertDataWithSerializedJsonString(Map data, TRouteResult routeResult, List status) { - Assert.assertEquals(data.get("code"), routeResult.getValue()); + if (routeResult == TRouteResult.OK) { + Assert.assertEquals(data.get("code"), 0); + } else { + Assert.assertEquals(data.get("code"), -1); + } + Assert.assertEquals(data.get("message"), routeResult.name()); if (status != null) { From 6cc114ed9f490247e8fa7306ac245c7771ea5859 Mon Sep 17 00:00:00 2001 From: Jongho Moon Date: Wed, 14 Oct 2015 10:31:07 +0900 Subject: [PATCH 3/4] Add ApiIdAwareAroundInterceptor for spring beans profiling --- .../ApiIdAwareAroundInterceptor.java | 24 ++++++ .../GroupedApiIdAwareAroundInterceptor.java | 66 ++++++++++++++ .../spring/beans/BeanMethodTransformer.java | 34 ++++++-- ...AbstractSpringBeanCreationInterceptor.java | 1 - .../interceptor/BeanMethodInterceptor.java | 77 +++++++++++++++++ .../interceptor/DummyMethodDescriptor.java | 85 +++++++++++++++++++ .../profiler/instrument/JavassistMethod.java | 10 +-- .../interceptor/InvokeAfterCodeGenerator.java | 16 +++- .../InvokeBeforeCodeGenerator.java | 14 ++- .../interceptor/InvokeCodeGenerator.java | 19 ++++- .../factory/AnnotatedInterceptorFactory.java | 4 + 11 files changed, 326 insertions(+), 24 deletions(-) create mode 100644 bootstrap-core/src/main/java/com/navercorp/pinpoint/bootstrap/interceptor/ApiIdAwareAroundInterceptor.java create mode 100644 bootstrap-core/src/main/java/com/navercorp/pinpoint/bootstrap/interceptor/group/GroupedApiIdAwareAroundInterceptor.java create mode 100644 plugins/spring/src/main/java/com/navercorp/pinpoint/plugin/spring/beans/interceptor/BeanMethodInterceptor.java create mode 100644 plugins/spring/src/main/java/com/navercorp/pinpoint/plugin/spring/beans/interceptor/DummyMethodDescriptor.java diff --git a/bootstrap-core/src/main/java/com/navercorp/pinpoint/bootstrap/interceptor/ApiIdAwareAroundInterceptor.java b/bootstrap-core/src/main/java/com/navercorp/pinpoint/bootstrap/interceptor/ApiIdAwareAroundInterceptor.java new file mode 100644 index 000000000..dba1fc17f --- /dev/null +++ b/bootstrap-core/src/main/java/com/navercorp/pinpoint/bootstrap/interceptor/ApiIdAwareAroundInterceptor.java @@ -0,0 +1,24 @@ +/** + * 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.bootstrap.interceptor; + +/** + * @author Jongho Moon + * + */ +public interface ApiIdAwareAroundInterceptor extends Interceptor { + void before(Object target, int apiId, Object[] args); + void after(Object target, int apiId, Object[] args, Object result, Throwable throwable); +} diff --git a/bootstrap-core/src/main/java/com/navercorp/pinpoint/bootstrap/interceptor/group/GroupedApiIdAwareAroundInterceptor.java b/bootstrap-core/src/main/java/com/navercorp/pinpoint/bootstrap/interceptor/group/GroupedApiIdAwareAroundInterceptor.java new file mode 100644 index 000000000..28f1375bb --- /dev/null +++ b/bootstrap-core/src/main/java/com/navercorp/pinpoint/bootstrap/interceptor/group/GroupedApiIdAwareAroundInterceptor.java @@ -0,0 +1,66 @@ +/* + * 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.bootstrap.interceptor.group; + +import com.navercorp.pinpoint.bootstrap.interceptor.ApiIdAwareAroundInterceptor; +import com.navercorp.pinpoint.bootstrap.logging.PLogger; +import com.navercorp.pinpoint.bootstrap.logging.PLoggerFactory; + +/** + * @author emeroad + */ +public class GroupedApiIdAwareAroundInterceptor implements ApiIdAwareAroundInterceptor { + private final PLogger logger = PLoggerFactory.getLogger(getClass()); + private final boolean debugEnabled = logger.isDebugEnabled(); + + private final ApiIdAwareAroundInterceptor delegate; + private final InterceptorGroup group; + private final ExecutionPolicy policy; + + public GroupedApiIdAwareAroundInterceptor(ApiIdAwareAroundInterceptor delegate, InterceptorGroup group, ExecutionPolicy policy) { + this.delegate = delegate; + this.group = group; + this.policy = policy; + } + + @Override + public void before(Object target, int apiId, Object[] args) { + InterceptorGroupInvocation transaction = group.getCurrentInvocation(); + + if (transaction.tryEnter(policy)) { + this.delegate.before(target, apiId, args); + } else { + if (debugEnabled) { + logger.debug("tryBefore() returns false: interceptorGroupTransaction: {}, executionPoint: {}. Skip interceptor {}", new Object[] {transaction, policy, delegate.getClass()} ); + } + } + } + + @Override + public void after(Object target, int apiId, Object[] args, Object result, Throwable throwable) { + InterceptorGroupInvocation transaction = group.getCurrentInvocation(); + + if (transaction.canLeave(policy)) { + this.delegate.after(target, apiId, args, result, throwable); + transaction.leave(policy); + } else { + if (debugEnabled) { + logger.debug("tryAfter() returns false: interceptorGroupTransaction: {}, executionPoint: {}. Skip interceptor {}", new Object[] {transaction, policy, delegate.getClass()} ); + } + } + } +} diff --git a/plugins/spring/src/main/java/com/navercorp/pinpoint/plugin/spring/beans/BeanMethodTransformer.java b/plugins/spring/src/main/java/com/navercorp/pinpoint/plugin/spring/beans/BeanMethodTransformer.java index 9153dc281..8d07b63be 100644 --- a/plugins/spring/src/main/java/com/navercorp/pinpoint/plugin/spring/beans/BeanMethodTransformer.java +++ b/plugins/spring/src/main/java/com/navercorp/pinpoint/plugin/spring/beans/BeanMethodTransformer.java @@ -18,20 +18,18 @@ import java.lang.reflect.Modifier; import java.security.ProtectionDomain; import java.util.Arrays; import java.util.List; +import java.util.concurrent.atomic.AtomicInteger; import com.navercorp.pinpoint.bootstrap.instrument.InstrumentClass; import com.navercorp.pinpoint.bootstrap.instrument.InstrumentException; import com.navercorp.pinpoint.bootstrap.instrument.InstrumentMethod; +import com.navercorp.pinpoint.bootstrap.instrument.Instrumentor; import com.navercorp.pinpoint.bootstrap.instrument.MethodFilter; import com.navercorp.pinpoint.bootstrap.instrument.MethodFilters; -import com.navercorp.pinpoint.bootstrap.instrument.Instrumentor; import com.navercorp.pinpoint.bootstrap.instrument.transformer.PinpointClassFileTransformer; -import com.navercorp.pinpoint.bootstrap.interceptor.BasicMethodInterceptor; import com.navercorp.pinpoint.bootstrap.logging.PLogger; import com.navercorp.pinpoint.bootstrap.logging.PLoggerFactory; -import static com.navercorp.pinpoint.common.util.VarArgs.va; - /** * @author Jongho Moon * @@ -43,10 +41,9 @@ public class BeanMethodTransformer implements PinpointClassFileTransformer { private final PLogger logger = PLoggerFactory.getLogger(getClass()); + private AtomicInteger interceptorId = new AtomicInteger(-1); + - /* (non-Javadoc) - * @see com.navercorp.pinpoint.bootstrap.plugin.transformer.PinpointClassFileTransformer#transform(com.navercorp.pinpoint.bootstrap.plugin.PinpointInstrument, java.lang.ClassLoader, java.lang.String, java.lang.Class, java.security.ProtectionDomain, byte[]) - */ @Override public byte[] transform(Instrumentor instrumentContext, ClassLoader loader, String className, Class classBeingRedefined, ProtectionDomain protectionDomain, byte[] classfileBuffer) throws InstrumentException { if (logger.isInfoEnabled()) { @@ -66,7 +63,7 @@ public class BeanMethodTransformer implements PinpointClassFileTransformer { logger.trace("### c={}, m={}, params={}", new Object[] {className, method.getName(), Arrays.toString(method.getParameterTypes())}); } - method.addInterceptor(BasicMethodInterceptor.class.getName(), va(SpringBeansConstants.SERVICE_TYPE)); + addInterceptor(method); } return target.toBytecode(); @@ -75,4 +72,25 @@ public class BeanMethodTransformer implements PinpointClassFileTransformer { return null; } } + + private void addInterceptor(InstrumentMethod targetMethod) throws InstrumentException { + int id = interceptorId.get(); + + if (id != -1) { + targetMethod.addInterceptor(id); + return; + } + + synchronized (interceptorId) { + id = interceptorId.get(); + + if (id != -1) { + targetMethod.addInterceptor(id); + return; + } + + id = targetMethod.addInterceptor("com.navercorp.pinpoint.plugin.spring.beans.interceptor.BeanMethodInterceptor"); + interceptorId.set(id); + } + } } diff --git a/plugins/spring/src/main/java/com/navercorp/pinpoint/plugin/spring/beans/interceptor/AbstractSpringBeanCreationInterceptor.java b/plugins/spring/src/main/java/com/navercorp/pinpoint/plugin/spring/beans/interceptor/AbstractSpringBeanCreationInterceptor.java index 2ee440fa6..06ab0a5e8 100644 --- a/plugins/spring/src/main/java/com/navercorp/pinpoint/plugin/spring/beans/interceptor/AbstractSpringBeanCreationInterceptor.java +++ b/plugins/spring/src/main/java/com/navercorp/pinpoint/plugin/spring/beans/interceptor/AbstractSpringBeanCreationInterceptor.java @@ -18,7 +18,6 @@ package com.navercorp.pinpoint.plugin.spring.beans.interceptor; import com.navercorp.pinpoint.bootstrap.instrument.Instrumentor; import com.navercorp.pinpoint.bootstrap.instrument.transformer.PinpointClassFileTransformer; -import com.navercorp.pinpoint.bootstrap.interceptor.Interceptor; import com.navercorp.pinpoint.bootstrap.logging.PLogger; import com.navercorp.pinpoint.bootstrap.logging.PLoggerFactory; diff --git a/plugins/spring/src/main/java/com/navercorp/pinpoint/plugin/spring/beans/interceptor/BeanMethodInterceptor.java b/plugins/spring/src/main/java/com/navercorp/pinpoint/plugin/spring/beans/interceptor/BeanMethodInterceptor.java new file mode 100644 index 000000000..8d42983a3 --- /dev/null +++ b/plugins/spring/src/main/java/com/navercorp/pinpoint/plugin/spring/beans/interceptor/BeanMethodInterceptor.java @@ -0,0 +1,77 @@ +/* + * 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.spring.beans.interceptor; + +import com.navercorp.pinpoint.bootstrap.context.SpanEventRecorder; +import com.navercorp.pinpoint.bootstrap.context.Trace; +import com.navercorp.pinpoint.bootstrap.context.TraceContext; +import com.navercorp.pinpoint.bootstrap.interceptor.ApiIdAwareAroundInterceptor; +import com.navercorp.pinpoint.bootstrap.logging.PLogger; +import com.navercorp.pinpoint.bootstrap.logging.PLoggerFactory; +import com.navercorp.pinpoint.plugin.spring.beans.SpringBeansConstants; + +/** + * + * @author netspider + * @author emeroad + */ +public class BeanMethodInterceptor implements ApiIdAwareAroundInterceptor { + + private final PLogger logger = PLoggerFactory.getLogger(BeanMethodInterceptor.class); + private final boolean isDebug = logger.isDebugEnabled(); + + private final TraceContext traceContext; + + public BeanMethodInterceptor(TraceContext traceContext) { + this.traceContext = traceContext; + } + + @Override + public void before(Object target, int apiId, Object[] args) { + if (isDebug) { + logger.beforeInterceptor(target, args); + } + + Trace trace = traceContext.currentTraceObject(); + if (trace == null) { + return; + } + + final SpanEventRecorder recorder = trace.traceBlockBegin(); + recorder.recordServiceType(SpringBeansConstants.SERVICE_TYPE); + } + + @Override + public void after(Object target, int apiId, Object[] args, Object result, Throwable throwable) { + if (isDebug) { + logger.afterInterceptor(target, args); + } + + Trace trace = traceContext.currentTraceObject(); + if (trace == null) { + return; + } + + try { + final SpanEventRecorder recorder = trace.currentSpanEventRecorder(); + recorder.recordApi(new DummyMethodDescriptor(apiId)); + recorder.recordException(throwable); + } finally { + trace.traceBlockEnd(); + } + } +} \ No newline at end of file diff --git a/plugins/spring/src/main/java/com/navercorp/pinpoint/plugin/spring/beans/interceptor/DummyMethodDescriptor.java b/plugins/spring/src/main/java/com/navercorp/pinpoint/plugin/spring/beans/interceptor/DummyMethodDescriptor.java new file mode 100644 index 000000000..fb9836e49 --- /dev/null +++ b/plugins/spring/src/main/java/com/navercorp/pinpoint/plugin/spring/beans/interceptor/DummyMethodDescriptor.java @@ -0,0 +1,85 @@ +/** + * 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.spring.beans.interceptor; + +import com.navercorp.pinpoint.bootstrap.context.MethodDescriptor; + +/** + * @author Jongho Moon + * + */ +public class DummyMethodDescriptor implements MethodDescriptor { + private final int apiId; + + public DummyMethodDescriptor(int apiId) { + this.apiId = apiId; + } + + @Override + public String getMethodName() { + return null; + } + + @Override + public String getClassName() { + return null; + } + + @Override + public String[] getParameterTypes() { + return null; + } + + @Override + public String[] getParameterVariableName() { + return null; + } + + @Override + public String getParameterDescriptor() { + return null; + } + + @Override + public int getLineNumber() { + return 0; + } + + @Override + public String getFullName() { + return null; + } + + @Override + public void setApiId(int apiId) { + + } + + @Override + public int getApiId() { + return apiId; + } + + @Override + public String getApiDescriptor() { + return null; + } + + @Override + public int getType() { + return 0; + } + +} diff --git a/profiler/src/main/java/com/navercorp/pinpoint/profiler/instrument/JavassistMethod.java b/profiler/src/main/java/com/navercorp/pinpoint/profiler/instrument/JavassistMethod.java index c28e798cc..785163a98 100644 --- a/profiler/src/main/java/com/navercorp/pinpoint/profiler/instrument/JavassistMethod.java +++ b/profiler/src/main/java/com/navercorp/pinpoint/profiler/instrument/JavassistMethod.java @@ -18,7 +18,6 @@ package com.navercorp.pinpoint.profiler.instrument; import java.lang.reflect.Method; -import com.navercorp.pinpoint.common.util.Asserts; import javassist.CannotCompileException; import javassist.CtBehavior; import javassist.CtClass; @@ -48,6 +47,7 @@ import com.navercorp.pinpoint.bootstrap.interceptor.annotation.Group; import com.navercorp.pinpoint.bootstrap.interceptor.group.ExecutionPolicy; import com.navercorp.pinpoint.bootstrap.interceptor.group.InterceptorGroup; import com.navercorp.pinpoint.bootstrap.interceptor.registry.InterceptorRegistry; +import com.navercorp.pinpoint.common.util.Asserts; import com.navercorp.pinpoint.profiler.context.DefaultMethodDescriptor; import com.navercorp.pinpoint.profiler.instrument.interceptor.InvokeAfterCodeGenerator; import com.navercorp.pinpoint.profiler.instrument.interceptor.InvokeBeforeCodeGenerator; @@ -72,7 +72,7 @@ public class JavassistMethod implements InstrumentMethod { this.interceptorRegistryBinder = interceptorRegistryBinder; this.behavior = behavior; this.declaringClass = declaringClass; - + String[] parameterVariableNames = JavaAssistUtils.getParameterVariableName(behavior); int lineNumber = JavaAssistUtils.getLineNumber(behavior); @@ -292,7 +292,7 @@ public class JavassistMethod implements InstrumentMethod { } - InvokeAfterCodeGenerator catchGenerator = new InvokeAfterCodeGenerator(interceptorId, interceptorClass, interceptorMethod, declaringClass, this, localVarsInitialized, true); + InvokeAfterCodeGenerator catchGenerator = new InvokeAfterCodeGenerator(interceptorId, interceptorClass, interceptorMethod, declaringClass, this, pluginContext.getTraceContext(), localVarsInitialized, true); String catchCode = catchGenerator.generate(); if (isDebug) { @@ -303,7 +303,7 @@ public class JavassistMethod implements InstrumentMethod { insertCatch(originalCodeOffset, catchCode, throwable, "$e"); - InvokeAfterCodeGenerator afterGenerator = new InvokeAfterCodeGenerator(interceptorId, interceptorClass, interceptorMethod, declaringClass, this, localVarsInitialized, false); + InvokeAfterCodeGenerator afterGenerator = new InvokeAfterCodeGenerator(interceptorId, interceptorClass, interceptorMethod, declaringClass, this, pluginContext.getTraceContext(), localVarsInitialized, false); final String afterCode = afterGenerator.generate(); if (isDebug) { @@ -324,7 +324,7 @@ public class JavassistMethod implements InstrumentMethod { return -1; } - InvokeBeforeCodeGenerator generator = new InvokeBeforeCodeGenerator(interceptorId, interceptorClass, interceptorMethod, declaringClass, this); + InvokeBeforeCodeGenerator generator = new InvokeBeforeCodeGenerator(interceptorId, interceptorClass, interceptorMethod, declaringClass, this, pluginContext.getTraceContext()); String beforeCode = generator.generate(); if (isDebug) { diff --git a/profiler/src/main/java/com/navercorp/pinpoint/profiler/instrument/interceptor/InvokeAfterCodeGenerator.java b/profiler/src/main/java/com/navercorp/pinpoint/profiler/instrument/interceptor/InvokeAfterCodeGenerator.java index f4ee22dea..c82aaad1c 100644 --- a/profiler/src/main/java/com/navercorp/pinpoint/profiler/instrument/interceptor/InvokeAfterCodeGenerator.java +++ b/profiler/src/main/java/com/navercorp/pinpoint/profiler/instrument/interceptor/InvokeAfterCodeGenerator.java @@ -16,6 +16,7 @@ package com.navercorp.pinpoint.profiler.instrument.interceptor; import java.lang.reflect.Method; +import com.navercorp.pinpoint.bootstrap.context.TraceContext; import com.navercorp.pinpoint.bootstrap.instrument.InstrumentClass; import com.navercorp.pinpoint.bootstrap.instrument.InstrumentMethod; @@ -33,8 +34,8 @@ public class InvokeAfterCodeGenerator extends InvokeCodeGenerator { private final boolean localVarsInitialized; private final boolean catchClause; - public InvokeAfterCodeGenerator(int interceptorId, Class interceptorClass, Method interceptorMethod, InstrumentClass targetClass, InstrumentMethod targetMethod, boolean localVarsInitialized, boolean catchClause) { - super(interceptorId, interceptorClass, targetMethod); + public InvokeAfterCodeGenerator(int interceptorId, Class interceptorClass, Method interceptorMethod, InstrumentClass targetClass, InstrumentMethod targetMethod, TraceContext traceContext, boolean localVarsInitialized, boolean catchCluase) { + super(interceptorId, interceptorClass, targetMethod, traceContext); this.interceptorId = interceptorId; this.interceptorMethod = interceptorMethod; @@ -110,7 +111,10 @@ public class InvokeAfterCodeGenerator extends InvokeCodeGenerator { case STATIC: appendStaticAfterArguments(builder); break; - case CUSTOM: + case API_ID_AWARE: + appendApiIdAwareAfterArguments(builder); + break; + case BASIC: appendCustomAfterArguments(builder); break; } @@ -123,7 +127,11 @@ public class InvokeAfterCodeGenerator extends InvokeCodeGenerator { private void appendStaticAfterArguments(CodeBuilder builder) { builder.format("%1$s, \"%2$s\", \"%3$s\", \"%4$s\", %5$s, %6$s, %7$s", getTarget(), targetClass.getName(), targetMethod.getName(), getParameterTypes(), getArguments(), getReturnValue(), getException()); } - + + private void appendApiIdAwareAfterArguments(CodeBuilder builder) { + builder.format("%1$s, %2$d, %3$s, %4$s, %5$s", getTarget(), getApiId(), getArguments(), getReturnValue(), getException()); + } + private void appendCustomAfterArguments(CodeBuilder builder) { final Class[] interceptorParamTypes = interceptorMethod.getParameterTypes(); diff --git a/profiler/src/main/java/com/navercorp/pinpoint/profiler/instrument/interceptor/InvokeBeforeCodeGenerator.java b/profiler/src/main/java/com/navercorp/pinpoint/profiler/instrument/interceptor/InvokeBeforeCodeGenerator.java index e5a7a8ced..a9c0be4df 100644 --- a/profiler/src/main/java/com/navercorp/pinpoint/profiler/instrument/interceptor/InvokeBeforeCodeGenerator.java +++ b/profiler/src/main/java/com/navercorp/pinpoint/profiler/instrument/interceptor/InvokeBeforeCodeGenerator.java @@ -16,6 +16,7 @@ package com.navercorp.pinpoint.profiler.instrument.interceptor; import java.lang.reflect.Method; +import com.navercorp.pinpoint.bootstrap.context.TraceContext; import com.navercorp.pinpoint.bootstrap.instrument.InstrumentClass; import com.navercorp.pinpoint.bootstrap.instrument.InstrumentMethod; @@ -28,8 +29,8 @@ public class InvokeBeforeCodeGenerator extends InvokeCodeGenerator { private final Method interceptorMethod; private final InstrumentClass targetClass; - public InvokeBeforeCodeGenerator(int interceptorId, Class interceptorClass, Method interceptorMethod, InstrumentClass targetClass, InstrumentMethod targetMethod) { - super(interceptorId, interceptorClass, targetMethod); + public InvokeBeforeCodeGenerator(int interceptorId, Class interceptorClass, Method interceptorMethod, InstrumentClass targetClass, InstrumentMethod targetMethod, TraceContext traceContext) { + super(interceptorId, interceptorClass, targetMethod, traceContext); this.interceptorId = interceptorId; this.interceptorMethod = interceptorMethod; @@ -72,7 +73,10 @@ public class InvokeBeforeCodeGenerator extends InvokeCodeGenerator { case STATIC: appendStaticBeforeArguments(builder); break; - case CUSTOM: + case API_ID_AWARE: + appendApiIdAwareBeforeArguments(builder); + break; + case BASIC: appendCustomBeforeArguments(builder); break; } @@ -86,6 +90,10 @@ public class InvokeBeforeCodeGenerator extends InvokeCodeGenerator { builder.format("%1$s, \"%2$s\", \"%3$s\", \"%4$s\", %5$s", getTarget(), targetClass.getName(), targetMethod.getName(), getParameterTypes(), getArguments()); } + private void appendApiIdAwareBeforeArguments(CodeBuilder builder) { + builder.format("%1$s, %2$d, %3$s", getTarget(), getApiId(), getArguments()); + } + private void appendCustomBeforeArguments(CodeBuilder builder) { Class[] paramTypes = interceptorMethod.getParameterTypes(); diff --git a/profiler/src/main/java/com/navercorp/pinpoint/profiler/instrument/interceptor/InvokeCodeGenerator.java b/profiler/src/main/java/com/navercorp/pinpoint/profiler/instrument/interceptor/InvokeCodeGenerator.java index 2995f0d6e..255fdc15c 100644 --- a/profiler/src/main/java/com/navercorp/pinpoint/profiler/instrument/interceptor/InvokeCodeGenerator.java +++ b/profiler/src/main/java/com/navercorp/pinpoint/profiler/instrument/interceptor/InvokeCodeGenerator.java @@ -16,8 +16,11 @@ package com.navercorp.pinpoint.profiler.instrument.interceptor; import java.lang.reflect.Modifier; +import com.navercorp.pinpoint.bootstrap.context.MethodDescriptor; +import com.navercorp.pinpoint.bootstrap.context.TraceContext; import com.navercorp.pinpoint.bootstrap.instrument.InstrumentMethod; import com.navercorp.pinpoint.bootstrap.interceptor.AfterInterceptor; +import com.navercorp.pinpoint.bootstrap.interceptor.ApiIdAwareAroundInterceptor; import com.navercorp.pinpoint.bootstrap.interceptor.BeforeInterceptor; import com.navercorp.pinpoint.bootstrap.interceptor.InterceptorInvokerHelper; import com.navercorp.pinpoint.bootstrap.interceptor.StaticAroundInterceptor; @@ -29,27 +32,31 @@ import com.navercorp.pinpoint.profiler.util.JavaAssistUtils; * */ public class InvokeCodeGenerator { + private final TraceContext traceContext; protected final Class interceptorClass; protected final InstrumentMethod targetMethod; protected final int interceptorId; protected final Type type; - public InvokeCodeGenerator(int interceptorId, Class interceptorClass, InstrumentMethod targetMethod) { + public InvokeCodeGenerator(int interceptorId, Class interceptorClass, InstrumentMethod targetMethod, TraceContext traceContext) { this.interceptorClass = interceptorClass; this.targetMethod = targetMethod; this.interceptorId = interceptorId; + this.traceContext = traceContext; if (BeforeInterceptor.class.isAssignableFrom(interceptorClass) || AfterInterceptor.class.isAssignableFrom(interceptorClass)) { type = Type.ARRAY_ARGS; } else if (StaticAroundInterceptor.class.isAssignableFrom(interceptorClass)) { type = Type.STATIC; + } else if (ApiIdAwareAroundInterceptor.class.isAssignableFrom(interceptorClass)) { + type = Type.API_ID_AWARE; } else { - type = Type.CUSTOM; + type = Type.BASIC; } } protected enum Type { - ARRAY_ARGS, STATIC, CUSTOM + ARRAY_ARGS, STATIC, BASIC, API_ID_AWARE } protected String getInterceptorType() { @@ -73,6 +80,12 @@ public class InvokeCodeGenerator { return "$args"; } + protected int getApiId() { + MethodDescriptor descriptor = targetMethod.getDescriptor(); + int apiId = traceContext.cacheApi(descriptor); + return apiId; + } + protected String getInterceptorInvokerHelperClassName() { return InterceptorInvokerHelper.class.getName(); } diff --git a/profiler/src/main/java/com/navercorp/pinpoint/profiler/interceptor/factory/AnnotatedInterceptorFactory.java b/profiler/src/main/java/com/navercorp/pinpoint/profiler/interceptor/factory/AnnotatedInterceptorFactory.java index 517ca2da3..f787b8e3d 100644 --- a/profiler/src/main/java/com/navercorp/pinpoint/profiler/interceptor/factory/AnnotatedInterceptorFactory.java +++ b/profiler/src/main/java/com/navercorp/pinpoint/profiler/interceptor/factory/AnnotatedInterceptorFactory.java @@ -26,6 +26,7 @@ import com.navercorp.pinpoint.bootstrap.interceptor.AfterInterceptor2; import com.navercorp.pinpoint.bootstrap.interceptor.AfterInterceptor3; import com.navercorp.pinpoint.bootstrap.interceptor.AfterInterceptor4; import com.navercorp.pinpoint.bootstrap.interceptor.AfterInterceptor5; +import com.navercorp.pinpoint.bootstrap.interceptor.ApiIdAwareAroundInterceptor; import com.navercorp.pinpoint.bootstrap.interceptor.AroundInterceptor; import com.navercorp.pinpoint.bootstrap.interceptor.AroundInterceptor0; import com.navercorp.pinpoint.bootstrap.interceptor.AroundInterceptor1; @@ -44,6 +45,7 @@ import com.navercorp.pinpoint.bootstrap.interceptor.Interceptor; import com.navercorp.pinpoint.bootstrap.interceptor.StaticAroundInterceptor; import com.navercorp.pinpoint.bootstrap.interceptor.annotation.Group; import com.navercorp.pinpoint.bootstrap.interceptor.group.ExecutionPolicy; +import com.navercorp.pinpoint.bootstrap.interceptor.group.GroupedApiIdAwareAroundInterceptor; import com.navercorp.pinpoint.bootstrap.interceptor.group.GroupedInterceptor; import com.navercorp.pinpoint.bootstrap.interceptor.group.GroupedInterceptor0; import com.navercorp.pinpoint.bootstrap.interceptor.group.GroupedInterceptor1; @@ -136,6 +138,8 @@ public class AnnotatedInterceptorFactory implements InterceptorFactory { return new GroupedInterceptor((BeforeInterceptor)interceptor, null, group, policy); } else if (interceptor instanceof AfterInterceptor) { return new GroupedInterceptor(null, (AfterInterceptor)interceptor, group, policy); + } else if (interceptor instanceof ApiIdAwareAroundInterceptor) { + return new GroupedApiIdAwareAroundInterceptor((ApiIdAwareAroundInterceptor)interceptor, group, policy); } throw new IllegalArgumentException("Unexpected interceptor type: " + interceptor.getClass()); From 254e5cfec71fdef7d3e9e45d4f42ec42d82000f5 Mon Sep 17 00:00:00 2001 From: Jongho Moon Date: Wed, 14 Oct 2015 10:55:47 +0900 Subject: [PATCH 4/4] fix signiture mismatch after rebase --- .../interceptor/InvokeAfterCodeGenerator.java | 2 +- .../InvokeAfterCodeGeneratorTest.java | 33 ++++++++++++------- 2 files changed, 22 insertions(+), 13 deletions(-) diff --git a/profiler/src/main/java/com/navercorp/pinpoint/profiler/instrument/interceptor/InvokeAfterCodeGenerator.java b/profiler/src/main/java/com/navercorp/pinpoint/profiler/instrument/interceptor/InvokeAfterCodeGenerator.java index c82aaad1c..8b95774aa 100644 --- a/profiler/src/main/java/com/navercorp/pinpoint/profiler/instrument/interceptor/InvokeAfterCodeGenerator.java +++ b/profiler/src/main/java/com/navercorp/pinpoint/profiler/instrument/interceptor/InvokeAfterCodeGenerator.java @@ -34,7 +34,7 @@ public class InvokeAfterCodeGenerator extends InvokeCodeGenerator { private final boolean localVarsInitialized; private final boolean catchClause; - public InvokeAfterCodeGenerator(int interceptorId, Class interceptorClass, Method interceptorMethod, InstrumentClass targetClass, InstrumentMethod targetMethod, TraceContext traceContext, boolean localVarsInitialized, boolean catchCluase) { + public InvokeAfterCodeGenerator(int interceptorId, Class interceptorClass, Method interceptorMethod, InstrumentClass targetClass, InstrumentMethod targetMethod, TraceContext traceContext, boolean localVarsInitialized, boolean catchClause) { super(interceptorId, interceptorClass, targetMethod, traceContext); this.interceptorId = interceptorId; diff --git a/profiler/src/test/java/com/navercorp/pinpoint/profiler/instrument/interceptor/InvokeAfterCodeGeneratorTest.java b/profiler/src/test/java/com/navercorp/pinpoint/profiler/instrument/interceptor/InvokeAfterCodeGeneratorTest.java index f9e81e636..17c33c87a 100644 --- a/profiler/src/test/java/com/navercorp/pinpoint/profiler/instrument/interceptor/InvokeAfterCodeGeneratorTest.java +++ b/profiler/src/test/java/com/navercorp/pinpoint/profiler/instrument/interceptor/InvokeAfterCodeGeneratorTest.java @@ -16,19 +16,21 @@ package com.navercorp.pinpoint.profiler.instrument.interceptor; -import com.navercorp.pinpoint.bootstrap.instrument.InstrumentClass; -import com.navercorp.pinpoint.bootstrap.instrument.InstrumentMethod; -import com.navercorp.pinpoint.bootstrap.interceptor.AroundInterceptor0; -import com.navercorp.pinpoint.bootstrap.interceptor.AroundInterceptor3; +import static org.mockito.Mockito.*; + +import java.lang.reflect.Method; + import org.junit.Assert; import org.junit.Test; import org.mockito.Mockito; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import java.lang.reflect.Method; - -import static org.mockito.Mockito.mock; +import com.navercorp.pinpoint.bootstrap.context.TraceContext; +import com.navercorp.pinpoint.bootstrap.instrument.InstrumentClass; +import com.navercorp.pinpoint.bootstrap.instrument.InstrumentMethod; +import com.navercorp.pinpoint.bootstrap.interceptor.AroundInterceptor0; +import com.navercorp.pinpoint.bootstrap.interceptor.AroundInterceptor3; /** * @author emeroad @@ -50,9 +52,11 @@ public class InvokeAfterCodeGeneratorTest { Mockito.when(mockMethod.getName()).thenReturn("TestMethod"); Mockito.when(mockMethod.getParameterTypes()).thenReturn(new String[]{"java.lang.Object", "java.lang.Object", "java.lang.Object"}); Mockito.when(mockMethod.getReturnType()).thenReturn("java.lang.Object"); + + TraceContext context = mock(TraceContext.class); - final InvokeAfterCodeGenerator invokeAfterCodeGenerator = new InvokeAfterCodeGenerator(100, aroundInterceptor3Class, interceptorAfter, mockClass, mockMethod, false, true); + final InvokeAfterCodeGenerator invokeAfterCodeGenerator = new InvokeAfterCodeGenerator(100, aroundInterceptor3Class, interceptorAfter, mockClass, mockMethod, context, false, true); final String generate = invokeAfterCodeGenerator.generate(); logger.debug("testGenerate_AroundInterceptor3_catchClause:{}", generate); @@ -78,8 +82,9 @@ public class InvokeAfterCodeGeneratorTest { Mockito.when(mockMethod.getParameterTypes()).thenReturn(new String[]{"java.lang.Object", "java.lang.Object", "java.lang.Object"}); Mockito.when(mockMethod.getReturnType()).thenReturn("java.lang.Object"); + TraceContext context = mock(TraceContext.class); - final InvokeAfterCodeGenerator invokeAfterCodeGenerator = new InvokeAfterCodeGenerator(100, aroundInterceptor3Class, interceptorAfter, mockClass, mockMethod, false, false); + final InvokeAfterCodeGenerator invokeAfterCodeGenerator = new InvokeAfterCodeGenerator(100, aroundInterceptor3Class, interceptorAfter, mockClass, mockMethod, context, false, false); final String generate = invokeAfterCodeGenerator.generate(); logger.debug("testGenerate_AroundInterceptor3_NoCatchClause:{}", generate); @@ -104,9 +109,11 @@ public class InvokeAfterCodeGeneratorTest { Mockito.when(mockMethod.getName()).thenReturn("TestMethod"); Mockito.when(mockMethod.getParameterTypes()).thenReturn(new String[]{"java.lang.Object", "java.lang.Object"}); Mockito.when(mockMethod.getReturnType()).thenReturn("java.lang.Object"); + + TraceContext context = mock(TraceContext.class); - final InvokeAfterCodeGenerator invokeAfterCodeGenerator = new InvokeAfterCodeGenerator(100, aroundInterceptor3Class, interceptorAfter, mockClass, mockMethod, false, true); + final InvokeAfterCodeGenerator invokeAfterCodeGenerator = new InvokeAfterCodeGenerator(100, aroundInterceptor3Class, interceptorAfter, mockClass, mockMethod, context, false, true); final String generate = invokeAfterCodeGenerator.generate(); logger.debug("testGenerate_AroundInterceptor3_methodParam2:{}", generate); @@ -132,8 +139,9 @@ public class InvokeAfterCodeGeneratorTest { Mockito.when(mockMethod.getParameterTypes()).thenReturn(new String[]{"java.lang.Object", "java.lang.Object", "java.lang.Object", "java.lang.Object"}); Mockito.when(mockMethod.getReturnType()).thenReturn("java.lang.Object"); + TraceContext context = mock(TraceContext.class); - final InvokeAfterCodeGenerator invokeAfterCodeGenerator = new InvokeAfterCodeGenerator(100, aroundInterceptor3Class, interceptorAfter, mockClass, mockMethod, false, true); + final InvokeAfterCodeGenerator invokeAfterCodeGenerator = new InvokeAfterCodeGenerator(100, aroundInterceptor3Class, interceptorAfter, mockClass, mockMethod, context, false, true); final String generate = invokeAfterCodeGenerator.generate(); logger.debug("testGenerate_AroundInterceptor3_methodParam4:{}", generate); @@ -161,8 +169,9 @@ public class InvokeAfterCodeGeneratorTest { Mockito.when(mockMethod.getParameterTypes()).thenReturn(new String[]{}); Mockito.when(mockMethod.getReturnType()).thenReturn("java.lang.Object"); + TraceContext context = mock(TraceContext.class); - final InvokeAfterCodeGenerator invokeAfterCodeGenerator = new InvokeAfterCodeGenerator(100, aroundInterceptor3Class, interceptorAfter, mockClass, mockMethod, false, true); + final InvokeAfterCodeGenerator invokeAfterCodeGenerator = new InvokeAfterCodeGenerator(100, aroundInterceptor3Class, interceptorAfter, mockClass, mockMethod, context, false, true); final String generate = invokeAfterCodeGenerator.generate(); logger.debug("testGenerate_AroundInterceptor0:{}", generate);