0

We started auditing our clients requests on the server by its CRUD operation.

Instead of calling the audit class to log the request, we want to use a CXF Filter to pre-process & post-process each request. What do I mean?

We want to audit every update request (which contains a JSON) with its old value (before the change, a GET operation to the DB), but log it into the logger only after the request completed successfully.

We are already using this method for changing the response body when an exception is thrown (by using a jaxsrs:providers tag and an ExceptionMapper implementation), but I can't understand how to use it for pre/post processing of requests.

Thanks!


UPDATE:

Well I kinda understood what I need to do to make a CXF Interceptor:

  • Create a class extending AbstractPhaseInterceptor
  • In your class constructor call super(Phase.[the-needed-pahse]) to decide when will your interceptor run (here is a list of all incoming/outgoing phases)
  • Override handleMessage as it is the inherited method, here you'll write your logic
  • Add the interceptor to the web.xml file of your project

For example:

public void handleMessage(Message request) throws Fault {       
    try {
        InputStream is = request.getContent(InputStream.class);
        String reqBodyAsString = org.apache.commons.io.IOUtils.toString(is, "UTF-8");
        IOUtils.closeQuietly(is);       
        // Do the logic
        is = org.apache.commons.io.IOUtils.toInputStream(reqBodyAsString, "UTF-8");
        request.setContent(InputStream.class, is);
        org.apache.commons.io.IOUtils.closeQuietly(is);
    } 
    catch (IOException ioe) {
        ioe.printStackTrace();
    }
}

And the web.xml file:

<jaxrs:server id="[id]" address="[URL]">
    <jaxrs:inInterceptors><!-- because we want it to run BEFORE the request logic -->
        <ref bean="[interceptor-id]" />
    </jaxrs:inInterceptors>
</jaxrs:server>

<bean id="[interceptor-id]" class="[package-path.interceptor-class-name]"/>

You can see that after reading the content I write it back again into the request. That is because I get an exception saying my body is null.

And this is where I'm stuck, I have an interceptor but after reading the request-body, it seems that the logic can't be continued.

LioRz
  • 835
  • 1
  • 9
  • 17
  • Have you thought about doing this right before you insert/update to the database (instead of doing it at HTTP interceptor) transactionally? Are you using any ORM frameworks ? If you are using hibernate, there are ways to tap into the events and use that to create Audit entries. Example: https://www.mkyong.com/hibernate/hibernate-interceptor-example-audit-log/ – so-random-dude Nov 01 '16 at 14:45
  • @random_dude We're not using a standard DB, and we don't want to audit each and every operation only specific operations sent from a specific URL. Before the write-operation we don't have all the data we want to log about the transaction, we need to make it a little bit higher in the chain – LioRz Nov 01 '16 at 14:50
  • Why do you close the input stream right after setting it? org.apache.commons.io.IOUtils.closeQuietly(is);' – TMtech Nov 03 '16 at 12:37
  • @TMtech Because I don't really need it anymore, I checked and it doesn't have any change on the extracted String. BTW the set operation succeeds, I tried to extract it again after the setContent() and it's there, so I really don't know why I get a null on the request-logic (after the interceptor finishes) – LioRz Nov 03 '16 at 14:01
  • @Lior What do you mean by _I don't need it any more_? That input stream is the content of the HTTP request body. If you close input stream in a filter or an interceptor, request body will not be passed to resource methods. Remove that line (`org.apache.commons.io.IOUtils.closeQuietly(is);`) and retry. If it did not solve the problem, update the question with the error message(stack trace or something) – TMtech Nov 03 '16 at 19:04

1 Answers1

0

You are consuming the request stream. You need to cache it before logging, and set a new stream in CXF message

public void handleMessage(Message message) {
      //Get the message body into payload[] and set a new non-consumed  inputStream into Message
      InputStream in = message.getContent(InputStream.class);
      byte payload[] = IOUtils.readBytesFromStream(in); //log this
      ByteArrayInputStream bin = new ByteArrayInputStream(payload);
    message.setContent(InputStream.class, bin);
 }

Close streams is not needed. Note also you can use CXF default logging interceptors https://stackoverflow.com/a/37820362/6371459

Community
  • 1
  • 1
pedrofb
  • 37,271
  • 5
  • 94
  • 142