DumpOutInterceptor.java

  1. /*
  2.  * GovWay - A customizable API Gateway
  3.  * https://govway.org
  4.  *
  5.  * Copyright (c) 2005-2025 Link.it srl (https://link.it).
  6.  *
  7.  * This program is free software: you can redistribute it and/or modify
  8.  * it under the terms of the GNU General Public License version 3, as published by
  9.  * the Free Software Foundation.
  10.  *
  11.  * This program is distributed in the hope that it will be useful,
  12.  * but WITHOUT ANY WARRANTY; without even the implied warranty of
  13.  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  14.  * GNU General Public License for more details.
  15.  *
  16.  * You should have received a copy of the GNU General Public License
  17.  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
  18.  *
  19.  */

  20. package org.openspcoop2.utils.service.context.dump;

  21. import java.io.OutputStream;
  22. import java.util.HashSet;
  23. import java.util.Set;

  24. import org.apache.cxf.common.injection.NoJSR250Annotations;
  25. import org.apache.cxf.common.util.StringUtils;
  26. import org.apache.cxf.ext.logging.event.DefaultLogEventMapper;
  27. import org.apache.cxf.ext.logging.event.LogEvent;
  28. import org.apache.cxf.ext.logging.event.LogEventSender;
  29. import org.apache.cxf.interceptor.Fault;
  30. import org.apache.cxf.io.CacheAndWriteOutputStream;
  31. import org.apache.cxf.io.CachedOutputStream;
  32. import org.apache.cxf.io.CachedOutputStreamCallback;
  33. import org.apache.cxf.message.Message;
  34. import org.openspcoop2.utils.LoggerWrapperFactory;
  35. import org.openspcoop2.utils.service.context.server.ServerConfig;


  36. /**
  37.  * DumpOutInterceptor
  38.  *
  39.  * @author Lorenzo Nardi (nardi@link.it)
  40.  * @author $Author$
  41.  * @version $Rev$, $Date$
  42.  */
  43. @NoJSR250Annotations
  44. public class DumpOutInterceptor extends org.apache.cxf.ext.logging.LoggingOutInterceptor {

  45.     private DumpConfig dumpConfig;
  46.     private ServerConfig serverConfig;
  47.    
  48.     public DumpOutInterceptor() {
  49.         super();
  50.     }

  51.     public DumpConfig getDumpConfig() {
  52.         return this.dumpConfig;
  53.     }
  54.     public void setDumpConfig(DumpConfig dumpConfig) {
  55.         this.dumpConfig = dumpConfig;
  56.         if(dumpConfig.getLimit()!=null) {
  57.             super.setLimit(dumpConfig.getLimit());
  58.         }
  59.         else {
  60.             super.setLimit(-1);
  61.         }
  62.     }
  63.    
  64.     public ServerConfig getServerConfig() {
  65.         return this.serverConfig;
  66.     }
  67.     public void setServerConfig(ServerConfig serverConfig) {
  68.         this.serverConfig = serverConfig;
  69.     }
  70.    
  71.     @Override
  72.     public void handleMessage(Message message) throws Fault {
  73.        
  74.         try {
  75.        
  76.             final OutputStream os = message.getContent(OutputStream.class);
  77.             if (os != null) {
  78.                 LoggingCallback callback = new LoggingCallback(this.sender, message, os, this.limit, this.dumpConfig, this.serverConfig);
  79.                 message.setContent(OutputStream.class, internal_createCachingOut(message, os, callback));
  80.             }
  81.            
  82.         } catch (Throwable e) {
  83.             LoggerWrapperFactory.getLogger(DumpInInterceptor.class).error(e.getMessage(),e);
  84.             throw new Fault(e);
  85.         }
  86.     }

  87.     private OutputStream internal_createCachingOut(Message message, final OutputStream os, CachedOutputStreamCallback callback) {
  88.         final CacheAndWriteOutputStream newOut = new CacheAndWriteOutputStream(os);
  89.         if (this.threshold > 0) {
  90.             newOut.setThreshold(this.threshold);
  91.         }
  92.         if (this.limit > 0) {
  93.             // make the limit for the cache greater than the limit for the truncated payload in the log event,
  94.             // this is necessary for finding out that the payload was truncated
  95.             //(see boolean isTruncated = cos.size() > limit && limit != -1;)  in method copyPayload
  96.             newOut.setCacheLimit(internal_getCacheLimit());
  97.         }
  98.         newOut.registerCallback(callback);
  99.         return newOut;
  100.     }

  101.     private int internal_getCacheLimit() {
  102.         if (this.limit == Integer.MAX_VALUE) {
  103.             return this.limit;
  104.         }
  105.         return this.limit + 1;
  106.     }

  107.     public class LoggingCallback implements CachedOutputStreamCallback {

  108.         private final Message message;
  109.         private final OutputStream origStream;
  110.         private final int lim;
  111.         @SuppressWarnings("unused")
  112.         private LogEventSender sender;
  113.         private DumpConfig dumpConfig;
  114.         private ServerConfig serverConfig;

  115.         public LoggingCallback(final LogEventSender sender, final Message msg, final OutputStream os, int limit,
  116.                 DumpConfig dumpConfig, ServerConfig serverConfig) {
  117.             this.sender = sender;
  118.             this.message = msg;
  119.             this.origStream = os;
  120.             this.lim = limit == -1 ? Integer.MAX_VALUE : limit;
  121.             this.dumpConfig = dumpConfig;
  122.             this.serverConfig = serverConfig;
  123.         }

  124.         @Override
  125.         public void onFlush(CachedOutputStream cos) {

  126.         }

  127.         @Override
  128.         public void onClose(CachedOutputStream cos) {
  129.            
  130.             try {
  131.            
  132.                 Set<String> sensitiveProtocolHeaders = new HashSet<String>();
  133.                 final LogEvent event = new DefaultLogEventMapper().map(this.message, sensitiveProtocolHeaders);
  134.                 if (shouldLogContent(event)) {
  135.                     copyPayload(cos, event);
  136.                 } else {
  137.                     event.setPayload(CONTENT_SUPPRESSED);
  138.                 }
  139.    
  140.                 DumpUtilities utilities = null;
  141.                 if(this.serverConfig!=null) {
  142.                     this.serverConfig.setDumpConfig(this.dumpConfig); // update
  143.                     utilities = new DumpUtilities(this.serverConfig);
  144.                 }
  145.                 else {
  146.                     utilities = new DumpUtilities(this.dumpConfig);
  147.                 }
  148.                
  149.                 DumpResponse response = new DumpResponse();
  150.                
  151.                 if(event.getPayload()!=null) {
  152.                     response.setPayload(event.getPayload().getBytes());
  153.                 }
  154.                 response.setContentType(event.getContentType());
  155.                 try {
  156.                     if(event.getResponseCode()!=null) {
  157.                         response.setResponseCode(Integer.parseInt(event.getResponseCode()));
  158.                     }
  159.                 }catch(Throwable t) {
  160.                 }
  161.                 response.setHeaders(event.getHeaders());
  162.                
  163.                 utilities.processAfterSend(response);
  164.                
  165.                 try {
  166.                     // empty out the cache
  167.                     cos.lockOutputStream();
  168.                     cos.resetOut(null, false);
  169.                 } catch (Exception ex) {
  170.                     // ignore
  171.                 }
  172.                 this.message.setContent(OutputStream.class, this.origStream);
  173.                                
  174.             } catch (Throwable e) {
  175.                 LoggerWrapperFactory.getLogger(DumpInInterceptor.class).error(e.getMessage(),e);
  176.                 throw new Fault(e);
  177.             }
  178.         }

  179.         private void copyPayload(CachedOutputStream cos, final LogEvent event) {
  180.             try {
  181.                 String encoding = (String) this.message.get(Message.ENCODING);
  182.                 StringBuilder payload = new StringBuilder();
  183.                 writePayload(payload, cos, encoding, event.getContentType());
  184.                 event.setPayload(payload.toString());
  185.                 boolean isTruncated = cos.size() > this.lim && this.lim != -1;
  186.                 event.setTruncated(isTruncated);
  187.             } catch (Exception ex) {
  188.                 // ignore
  189.             }
  190.         }

  191.         protected void writePayload(StringBuilder builder, CachedOutputStream cos, String encoding, String contentType)
  192.                 throws Exception {
  193.             if (StringUtils.isEmpty(encoding)) {
  194.                 cos.writeCacheTo(builder, this.lim);
  195.             } else {
  196.                 cos.writeCacheTo(builder, encoding, this.lim);
  197.             }
  198.         }
  199.     }

  200. }