/******************************************************************************* * Copyright 2015 Software Evolution and Architecture Lab, University of Zurich * * 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 eu.cloudwave.wp5.feedback.eclipse.costs.core.builders.participants; import java.util.Arrays; import java.util.List; import java.util.Map; import java.util.Optional; import java.util.stream.Collectors; import org.eclipse.core.resources.IMarker; import org.eclipse.jdt.core.dom.ASTVisitor; import org.eclipse.jdt.core.dom.CompilationUnit; import org.eclipse.jdt.core.dom.MethodDeclaration; import com.google.common.collect.Maps; import eu.cloudwave.wp5.common.constants.Ids; import eu.cloudwave.wp5.common.dto.ApplicationDto; import eu.cloudwave.wp5.common.dto.costs.AggregatedIncomingRequestsDto; import eu.cloudwave.wp5.common.dto.costs.AggregatedMicroserviceRequestsDto; import eu.cloudwave.wp5.feedback.eclipse.base.core.builders.participants.FeedbackBuilderParticipant; import eu.cloudwave.wp5.feedback.eclipse.base.resources.core.java.FeedbackJavaFile; import eu.cloudwave.wp5.feedback.eclipse.base.resources.core.java.FeedbackJavaProject; import eu.cloudwave.wp5.feedback.eclipse.base.resources.markers.MarkerAttributes; import eu.cloudwave.wp5.feedback.eclipse.base.resources.markers.MarkerPosition; import eu.cloudwave.wp5.feedback.eclipse.base.resources.markers.MarkerSpecification; import eu.cloudwave.wp5.feedback.eclipse.costs.core.CostIds; import eu.cloudwave.wp5.feedback.eclipse.costs.core.markers.CostMarkerTypes; import eu.cloudwave.wp5.feedback.eclipse.costs.ui.hovers.CostContextBuilder; /** * A builder participant that is responsible to display warnings for microservice endpoints */ public class MicroserviceMethodDeclarationParticipant extends AbstractCostFeedbackBuilderParticipant implements FeedbackBuilderParticipant { /** * The name of the template which should be used to display the hover */ private final static String HOVER_TEMPLATE = "methodDeclaration"; /** * The name of the annotation which this MicroserviceMethodDeclarationParticipant cares about */ private String targetAnnotation = "@" + Ids.MICROSERVICE_ENDPOINT_ANNOTATION; /** * Building files in which microservice endpoints/methods are defined */ @Override protected void buildFile(FeedbackJavaProject project, FeedbackJavaFile javaFile, CompilationUnit astRoot) { // System.out.println("EndpointParticipant buildFile"); astRoot.accept(new ASTVisitor() { private AggregatedIncomingRequestsDto[] incomingRequests; private Map<String, AggregatedIncomingRequestsDto> incomingRequestByMethod; private AggregatedIncomingRequestsDto overallRequest; private AggregatedMicroserviceRequestsDto[] requests; @Override public boolean visit(MethodDeclaration node) { Optional<?> annotationCheck = checkMethodDeclarationAnnotation(node.modifiers()); if (annotationCheck.isPresent()) { final String serviceMethodIdentifier = extractAttributeValueFromAnnotation(annotationCheck.get().toString(), Ids.MICROSERVICE_DECLARATION_ANNOTATION_METHOD_ATTRIBUTE); final String serviceMethodName = node.getName().getIdentifier(); /* * Depending on the user's properties we create and add a marker... */ if (showMethodDeclarationHover) { // Marker Specification int startPosition = node.getStartPosition(); if (node.getJavadoc() != null) { startPosition += node.getJavadoc().getLength() + annotationIndent + 1; // +1 for line break } final int line = astRoot.getLineNumber(startPosition); final int endPosition = startPosition + targetAnnotation.length(); final MarkerPosition position = new MarkerPosition(line, startPosition, endPosition); final String markerInfoTitle = "Microservice Method " + node.getName().toString(); MarkerSpecification costMarker = MarkerSpecification.of(CostIds.COST_MARKER, position, IMarker.SEVERITY_INFO, CostMarkerTypes.METHOD_DECLARATION, markerInfoTitle); /* * Preparation of the content of the hover which is rendered by Freemarker. The template is specified above * and can be found in the OSGI-INF/l10n/templates folder. */ // @formatter:off costMarker = costMarker.and(MarkerAttributes.DESCRIPTION, templateHandler.getContent(HOVER_TEMPLATE, CostContextBuilder.init() .setTimeParameters(timeRangeFrom, timeRangeTo, aggregationInterval) .setApplication(getApplication()) .setRequestStats("incoming", getIncomingRequestsByMethod(serviceMethodIdentifier)) .setRequestStats("overall", getOverallRequests()) .add("serviceIdentifier", serviceIdentifier) // substring from the properties: (eu.cloudwave.samples.services.) currency .addIfNotNull("serviceMethod", serviceMethodIdentifier, serviceMethodName) .add("requests", getRequestsByCallee(serviceMethodIdentifier)) .build())); // @formatter:on addMarker(javaFile, costMarker); } } return true; // do not go further to children } /** * Requests by callee in order to display different callers * * @return AggregatedMicroserviceRequestsDto with min, avg and max */ private List<AggregatedMicroserviceRequestsDto> getRequestsByCallee(String serviceMethodIdentifier) { if (requests == null) { requests = feedbackHandlerClient.requestsByCallee(project, aggregationInterval, timeRangeFrom, timeRangeTo); } if (serviceMethodIdentifier != null && !serviceMethodIdentifier.isEmpty()) { return Arrays.stream(requests).filter(request -> request.getCalleeMethod().equals(serviceMethodIdentifier)).collect(Collectors.toList()); } else { return Arrays.stream(requests).collect(Collectors.toList()); } } /** * Overall incoming request statistics to the application of the current project in the workspace. * * @return AggregatedIncomingRequestsDto with min, avg and max */ private AggregatedIncomingRequestsDto getOverallRequests() { if (overallRequest == null) { overallRequest = feedbackHandlerClient.overallIncomingRequestsByIdentifier(project, aggregationInterval, timeRangeFrom, timeRangeTo); } return overallRequest; } /** * Incoming request statistics of the current project/service * * @return AggregatedMicroserviceRequestsDto with min, avg and max */ private AggregatedIncomingRequestsDto[] getIncomingRequests() { if (incomingRequests == null) { incomingRequests = feedbackHandlerClient.incomingRequestsByIdentifier(project, aggregationInterval, timeRangeFrom, timeRangeTo); } return incomingRequests; } private AggregatedIncomingRequestsDto getIncomingRequestsByMethod(String key) { if (incomingRequestByMethod == null) { incomingRequestByMethod = Maps.newHashMap(); for (AggregatedIncomingRequestsDto request : getIncomingRequests()) { incomingRequestByMethod.put(request.getMethod(), request); } } return incomingRequestByMethod.get(key); } /** * Returns application of the current project * * @return {@link ApplicationDto} */ private ApplicationDto getApplication() { ApplicationDto app = cache.get(project.getApplicationId()); if (app == null) { app = cache.addAndReturn(project.getApplicationId(), feedbackHandlerClient.currentApplication(project)); } return app; } private Optional<?> checkMethodDeclarationAnnotation(List<?> modifiers) { // we do not go through the whole list: // the filter is only going to be applied until we reach any valid element return modifiers.stream().filter(modifier -> modifier.toString().startsWith(targetAnnotation)).findAny(); } }); } }