added svcapi ui and camunda code
[it/otf.git] / otf-camunda / src / main / java / org / oran / otf / spring / configuration / OTFLoggingFeature.java
1 /*  Copyright (c) 2019 AT&T Intellectual Property.                             #\r
2 #                                                                              #\r
3 #   Licensed under the Apache License, Version 2.0 (the "License");            #\r
4 #   you may not use this file except in compliance with the License.           #\r
5 #   You may obtain a copy of the License at                                    #\r
6 #                                                                              #\r
7 #       http://www.apache.org/licenses/LICENSE-2.0                             #\r
8 #                                                                              #\r
9 #   Unless required by applicable law or agreed to in writing, software        #\r
10 #   distributed under the License is distributed on an "AS IS" BASIS,          #\r
11 #   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.   #\r
12 #   See the License for the specific language governing permissions and        #\r
13 #   limitations under the License.                                             #\r
14 ##############################################################################*/\r
15 \r
16 \r
17 package org.oran.otf.spring.configuration;\r
18 \r
19 import java.io.BufferedInputStream;\r
20 import java.io.ByteArrayOutputStream;\r
21 import java.io.FilterOutputStream;\r
22 import java.io.IOException;\r
23 import java.io.InputStream;\r
24 import java.io.OutputStream;\r
25 import java.net.URI;\r
26 import java.nio.charset.Charset;\r
27 import java.util.ArrayList;\r
28 import java.util.Base64;\r
29 import java.util.List;\r
30 import java.util.Objects;\r
31 import java.util.logging.Level;\r
32 import java.util.logging.Logger;\r
33 import javax.ws.rs.WebApplicationException;\r
34 import javax.ws.rs.client.ClientRequestContext;\r
35 import javax.ws.rs.client.ClientRequestFilter;\r
36 import javax.ws.rs.client.ClientResponseContext;\r
37 import javax.ws.rs.client.ClientResponseFilter;\r
38 import javax.ws.rs.container.ContainerRequestContext;\r
39 import javax.ws.rs.container.ContainerRequestFilter;\r
40 import javax.ws.rs.container.ContainerResponseContext;\r
41 import javax.ws.rs.container.ContainerResponseFilter;\r
42 import javax.ws.rs.core.FeatureContext;\r
43 import javax.ws.rs.core.MultivaluedMap;\r
44 import javax.ws.rs.ext.WriterInterceptor;\r
45 import javax.ws.rs.ext.WriterInterceptorContext;\r
46 import org.glassfish.jersey.logging.LoggingFeature;\r
47 import org.glassfish.jersey.message.MessageUtils;\r
48 \r
49 public class OTFLoggingFeature extends LoggingFeature implements ContainerRequestFilter, ContainerResponseFilter,\r
50         ClientRequestFilter, ClientResponseFilter, WriterInterceptor {\r
51 \r
52     private static final boolean printEntity = true;\r
53     private static final int maxEntitySize = 8 * 1024;\r
54     private final Logger logger = Logger.getLogger("OTFLoggingFeature");\r
55     private static final String ENTITY_LOGGER_PROPERTY = OTFLoggingFeature.class.getName();\r
56     private static final String NOTIFICATION_PREFIX = "* ";\r
57     private static final String REQUEST_PREFIX = "> ";\r
58     private static final String RESPONSE_PREFIX = "< ";\r
59     private static final String AUTHORIZATION = "Authorization";\r
60     private static final String EQUAL = " = ";\r
61     private static final String HEADERS_SEPARATOR = ", ";\r
62     private static List<String> requestHeaders;\r
63 \r
64     static {\r
65         requestHeaders = new ArrayList<>();\r
66         requestHeaders.add(AUTHORIZATION);\r
67     }\r
68 \r
69     public OTFLoggingFeature(Logger logger, Level level, Verbosity verbosity, Integer maxEntitySize) {\r
70         super(logger, level, verbosity, maxEntitySize);\r
71     }\r
72 \r
73     @Override\r
74     public boolean configure(FeatureContext context) {\r
75         context.register(this);\r
76         return true;\r
77     }\r
78 \r
79     private Object getEmail(Object authorization){\r
80         try{\r
81             String encoded = ((String) authorization).split(" ")[1];\r
82             String decoded =  new String(Base64.getDecoder().decode(encoded));\r
83             return decoded.split(":")[0];\r
84         }\r
85         catch (Exception e){\r
86             return authorization;\r
87         }\r
88     }\r
89 \r
90     @Override\r
91     public void filter(final ClientRequestContext context) {\r
92         final StringBuilder b = new StringBuilder();\r
93         printHeaders(b, context.getStringHeaders());\r
94         printRequestLine(b, "Sending client request", context.getMethod(), context.getUri());\r
95 \r
96         if (printEntity && context.hasEntity()) {\r
97             final OutputStream stream = new LoggingStream(b, context.getEntityStream());\r
98             context.setEntityStream(stream);\r
99             context.setProperty(ENTITY_LOGGER_PROPERTY, stream);\r
100             // not calling log(b) here - it will be called by the interceptor\r
101         } else {\r
102             log(b);\r
103         }\r
104     }\r
105 \r
106     @Override\r
107     public void filter(final ClientRequestContext requestContext, final ClientResponseContext responseContext) throws IOException {\r
108         final StringBuilder b = new StringBuilder();\r
109         printResponseLine(b, "Client response received", responseContext.getStatus());\r
110 \r
111         if (printEntity && responseContext.hasEntity()) {\r
112             responseContext.setEntityStream(logInboundEntity(b, responseContext.getEntityStream(),\r
113                     MessageUtils.getCharset(responseContext.getMediaType())));\r
114         }\r
115         log(b);\r
116     }\r
117 \r
118     @Override\r
119     public void filter(final ContainerRequestContext context) throws IOException {\r
120         final StringBuilder b = new StringBuilder();\r
121         printHeaders(b, context.getHeaders());\r
122         printRequestLine(b, "Server has received a request", context.getMethod(), context.getUriInfo().getRequestUri());\r
123 \r
124         if (printEntity && context.hasEntity()) {\r
125             context.setEntityStream(logInboundEntity(b, context.getEntityStream(), MessageUtils.getCharset(context.getMediaType())));\r
126         }\r
127         log(b);\r
128     }\r
129 \r
130     @Override\r
131     public void filter(final ContainerRequestContext requestContext, final ContainerResponseContext responseContext) {\r
132         final StringBuilder b = new StringBuilder();\r
133         printResponseLine(b, "Server responded with a response", responseContext.getStatus());\r
134 \r
135         if (printEntity && responseContext.hasEntity()) {\r
136             final OutputStream stream = new LoggingStream(b, responseContext.getEntityStream());\r
137             responseContext.setEntityStream(stream);\r
138             requestContext.setProperty(ENTITY_LOGGER_PROPERTY, stream);\r
139             // not calling log(b) here - it will be called by the interceptor\r
140         } else {\r
141             log(b);\r
142         }\r
143     }\r
144 \r
145     @Override\r
146     public void aroundWriteTo(final WriterInterceptorContext writerInterceptorContext) throws IOException, WebApplicationException {\r
147         final LoggingStream stream = (LoggingStream) writerInterceptorContext.getProperty(ENTITY_LOGGER_PROPERTY);\r
148         writerInterceptorContext.proceed();\r
149         if (stream != null) {\r
150             log(stream.getStringBuilder(MessageUtils.getCharset(writerInterceptorContext.getMediaType())));\r
151         }\r
152     }\r
153 \r
154     private static class LoggingStream extends FilterOutputStream {\r
155         private final StringBuilder b;\r
156         private final ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();\r
157 \r
158         LoggingStream(final StringBuilder b, final OutputStream inner) {\r
159             super(inner);\r
160 \r
161             this.b = b;\r
162         }\r
163 \r
164         StringBuilder getStringBuilder(Charset charset) {\r
165             // write entity to the builder\r
166             final byte[] entity = byteArrayOutputStream.toByteArray();\r
167 \r
168             b.append(new String(entity, 0, Math.min(entity.length, maxEntitySize), charset));\r
169             if (entity.length > maxEntitySize) {\r
170                 b.append("...more...");\r
171             }\r
172             b.append('\n');\r
173 \r
174             return b;\r
175         }\r
176 \r
177         public void write(final int i) throws IOException {\r
178             if (byteArrayOutputStream.size() <= maxEntitySize) {\r
179                 byteArrayOutputStream.write(i);\r
180             }\r
181             out.write(i);\r
182         }\r
183     }\r
184 \r
185     private void printHeaders(StringBuilder b, MultivaluedMap<String, String> headers) {\r
186         for (String header : requestHeaders) {\r
187             if (Objects.nonNull(headers.get(header))) {\r
188                 if(header.equalsIgnoreCase("Authorization")){\r
189                     b.append(header).append(EQUAL).append(getEmail(headers.get(header).get(0))).append(HEADERS_SEPARATOR);\r
190                 }\r
191                 else{\r
192                     b.append(header).append(EQUAL).append(headers.get(header)).append(HEADERS_SEPARATOR);\r
193                 }\r
194             }\r
195         }\r
196         int lastIndex = b.lastIndexOf(HEADERS_SEPARATOR);\r
197         if (lastIndex != -1) {\r
198             b.delete(lastIndex, lastIndex + HEADERS_SEPARATOR.length());\r
199             b.append("\n");\r
200         }\r
201     }\r
202 \r
203     private void log(final StringBuilder b) {\r
204         String message = b.toString();\r
205         if (logger != null) {\r
206             logger.info(message);\r
207         }\r
208     }\r
209 \r
210     private void printRequestLine(final StringBuilder b, final String note, final String method, final URI uri) {\r
211         b.append(NOTIFICATION_PREFIX)\r
212                 .append(note)\r
213                 .append(" on thread ").append(Thread.currentThread().getId())\r
214                 .append(REQUEST_PREFIX).append(method).append(" ")\r
215                 .append(uri.toASCIIString()).append("\n");\r
216     }\r
217 \r
218     private void printResponseLine(final StringBuilder b, final String note, final int status) {\r
219         b.append(NOTIFICATION_PREFIX)\r
220                 .append(note)\r
221                 .append(" on thread ").append(Thread.currentThread().getId())\r
222                 .append(RESPONSE_PREFIX)\r
223                 .append(Integer.toString(status))\r
224                 .append("\n");\r
225     }\r
226 \r
227     private InputStream logInboundEntity(final StringBuilder b, InputStream stream, final Charset charset) throws IOException {\r
228         if (!stream.markSupported()) {\r
229             stream = new BufferedInputStream(stream);\r
230         }\r
231         stream.mark(maxEntitySize + 1);\r
232         final byte[] entity = new byte[maxEntitySize + 1];\r
233         final int entitySize = stream.read(entity);\r
234         b.append(new String(entity, 0, Math.min(entitySize, maxEntitySize), charset));\r
235         if (entitySize > maxEntitySize) {\r
236             b.append("...more...");\r
237         }\r
238         b.append('\n');\r
239         stream.reset();\r
240         return stream;\r
241     }\r
242 }