/* * Copyright 2016-2017 the original author or authors. * * 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 org.glowroot.agent.plugin.servlet; import javax.annotation.Nullable; import org.glowroot.agent.plugin.api.Agent; import org.glowroot.agent.plugin.api.MessageSupplier; import org.glowroot.agent.plugin.api.ThreadContext; import org.glowroot.agent.plugin.api.TimerName; import org.glowroot.agent.plugin.api.TraceEntry; import org.glowroot.agent.plugin.api.weaving.BindParameter; import org.glowroot.agent.plugin.api.weaving.BindReceiver; import org.glowroot.agent.plugin.api.weaving.BindReturn; import org.glowroot.agent.plugin.api.weaving.BindThrowable; import org.glowroot.agent.plugin.api.weaving.BindTraveler; import org.glowroot.agent.plugin.api.weaving.Mixin; import org.glowroot.agent.plugin.api.weaving.OnBefore; import org.glowroot.agent.plugin.api.weaving.OnReturn; import org.glowroot.agent.plugin.api.weaving.OnThrow; import org.glowroot.agent.plugin.api.weaving.Pointcut; public class RequestDispatcherAspect { // the field and method names are verbose to avoid conflict since they will become fields // and methods in all classes that extend javax.servlet.RequestDispatcher @Mixin("javax.servlet.RequestDispatcher") public abstract static class RequestDispatcherImpl implements RequestDispatcherMixin { private @Nullable String glowroot$path; @Override public @Nullable String glowroot$getPath() { return glowroot$path; } @Override public void glowroot$setPath(@Nullable String path) { this.glowroot$path = path; } } // the method names are verbose to avoid conflict since they will become methods in all classes // that extend javax.servlet.RequestDispatcher public interface RequestDispatcherMixin { @Nullable String glowroot$getPath(); void glowroot$setPath(@Nullable String path); } @Pointcut(className = "javax.servlet.ServletRequest|javax.servlet.ServletContext", methodName = "getRequestDispatcher|getNamedDispatcher", methodParameterTypes = {"java.lang.String"}, nestingGroup = "servlet-inner-call") public static class GetParameterAdvice { @OnReturn public static void onReturn(@BindReturn @Nullable RequestDispatcherMixin requestDispatcher, @BindParameter @Nullable String path) { if (requestDispatcher == null) { // seems nothing sensible to do here other than ignore return; } requestDispatcher.glowroot$setPath(path); } } @Pointcut(className = "javax.servlet.RequestDispatcher", methodName = "forward|include", methodParameterTypes = {"javax.servlet.ServletRequest", "javax.servlet.ServletResponse"}, nestingGroup = "servlet-dispatch", timerName = "servlet dispatch") public static class DispatchAdvice { private static final TimerName timerName = Agent.getTimerName(DispatchAdvice.class); @OnBefore public static TraceEntry onBefore(ThreadContext context, @BindReceiver RequestDispatcherMixin requestDispatcher) { return context.startTraceEntry(MessageSupplier.create("servlet dispatch: {}", requestDispatcher.glowroot$getPath()), timerName); } @OnReturn public static void onReturn(@BindTraveler TraceEntry traceEntry) { traceEntry.end(); } @OnThrow public static void onThrow(@BindThrowable Throwable t, @BindTraveler TraceEntry traceEntry) { traceEntry.endWithError(t); } } }