简体   繁体   中英

Aspectj pointcut to capture a stream closing (trying to measure bytes sent / received via sockets)

I'm trying to make a basic aspect (as a proof-of-concept, mainly) that I can use to keep track of IO done over sockets.

The following code wraps any calls to get the input stream from a socket with a Commons-IO based CountingInputStream. That part works.

What's not working (and I suspect it is because I fubar'ed my pointcut definition) is getting the number of bytes out of the CountingInputStream.

The close / reset advice is never being hit. (I had it as an @Before advice prior to switching it to @Around - but that wasn't working either...)

(Once I get the basic thing working I do plan to clean it up a bit more too)

package com.foo.io;

import com.yammer.metrics.Metrics;
import com.yammer.metrics.core.Counter;
import com.yammer.metrics.core.Histogram;
import com.yammer.metrics.core.MetricName;
import com.yammer.metrics.core.MetricsRegistry;
import org.apache.commons.io.input.CountingInputStream;
import org.apache.commons.io.output.CountingOutputStream;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;

import java.io.*;
import java.net.Socket;


@Aspect
public class SocketStreamWrapper {
    // can trivially create a map keeping track of bytes sent to / from a given address if this is desired
    final Counter inboundByteCounter;
    final Histogram inboundByteHistogram;
    final Counter outboundByteCounter;
    final Histogram outboundByteHistogram;

    public SocketStreamWrapper() {
        inboundByteCounter = Metrics.defaultRegistry().newCounter(new MetricName("inbound", "bytes", "counted"));
        inboundByteHistogram = Metrics.defaultRegistry().newHistogram(new MetricName("inbound", "bytes", "histogram"), true);
        outboundByteCounter = Metrics.defaultRegistry().newCounter(new MetricName("outbound", "bytes", "counted"));
        outboundByteHistogram = Metrics.defaultRegistry().newHistogram(new MetricName("outbound", "bytes", "histogram"), true);
    }

    @Pointcut("call(* java.net.Socket.getInputStream()) && target(s)")
    void input(Socket s) {
    }

    @Pointcut("call(* CountingInputStream.close()) && this(cis)")
    void close(CountingInputStream cis) {
    }

    @Pointcut("call(* CountingInputStream.reset()) && this(cis)")
    void reset(CountingInputStream cis) {
    }

    @Pointcut("call(* CountingInputStream+.read*()) && this(cis)")
    void read(CountingInputStream cis) {

    }

    @Around("close(cis)")
    public void closeCountingStream(ProceedingJoinPoint jp, CountingInputStream cis) throws Throwable {
        inboundByteCounter.inc(cis.getByteCount());
        inboundByteHistogram.update(cis.getByteCount());
        cis.resetByteCount();
        jp.proceed();
    }

    @Around("input(s)")
    public Object wrapInputStream(ProceedingJoinPoint joinPoint,
                                  Socket s)
            throws Throwable {
        InputStream in = (InputStream) joinPoint.proceed();
        return new CountingInputStream(in);
    }

    @Pointcut("call(* java.net.Socket.getOutputStream()) && target(s)")
    void output(Socket s) {
    }

    @Around("output(s)")
    public Object wrapOutputStream(ProceedingJoinPoint joinPoint,
                                   Socket s)
            throws Throwable {
        OutputStream out = (OutputStream) joinPoint.proceed();
        return new CountingOutputStream(out);
    }
}

The reason that you need to use target instead of this is that the target condition indicates that the pointcut applies to the object that the method is being called upon, not the caller of the method. In this case the object that your close , reset and read pointcuts are targeting is the CountingInputStream.

The distinction between the two is outlined in: http://www.eclipse.org/aspectj/doc/next/progguide/semantics-pointcuts.html

As an additional point, you may want to have a subclass of CountingInputStream with a private or protected scope so that you can target it directly and avoid accidentally interacting with others using CountingInputStreams. Your implementation of closeCountingStream calls resetByteCount() which would cause confusion and delay for other use cases.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM