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