1 /*
2  * JBoss, Home of Professional Open Source.
3  * Copyright 2014 Red Hat, Inc., and individual contributors
4  * as indicated by the @author tags.
5  *
6  * Licensed under the Apache License, Version 2.0 (the "License");
7  * you may not use this file except in compliance with the License.
8  * You may obtain a copy of the License at
9  *
10  *     http://www.apache.org/licenses/LICENSE-2.0
11  *
12  *  Unless required by applicable law or agreed to in writing, software
13  *  distributed under the License is distributed on an "AS IS" BASIS,
14  *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15  *  See the License for the specific language governing permissions and
16  *  limitations under the License.
17  */

18
19 package io.undertow.server;
20
21 import io.undertow.conduits.ByteActivityCallback;
22 import io.undertow.util.StatusCodes;
23
24 import java.util.concurrent.atomic.AtomicLongFieldUpdater;
25
26 /**
27  * @author Stuart Douglas
28  */

29 public class ConnectorStatisticsImpl implements ConnectorStatistics {
30
31     private static final AtomicLongFieldUpdater<ConnectorStatisticsImpl> requestCountUpdater = AtomicLongFieldUpdater.newUpdater(ConnectorStatisticsImpl.class"requestCount");
32     private static final AtomicLongFieldUpdater<ConnectorStatisticsImpl> bytesSentUpdater = AtomicLongFieldUpdater.newUpdater(ConnectorStatisticsImpl.class"bytesSent");
33     private static final AtomicLongFieldUpdater<ConnectorStatisticsImpl> bytesReceivedUpdater = AtomicLongFieldUpdater.newUpdater(ConnectorStatisticsImpl.class"bytesReceived");
34     private static final AtomicLongFieldUpdater<ConnectorStatisticsImpl> errorCountUpdater = AtomicLongFieldUpdater.newUpdater(ConnectorStatisticsImpl.class"errorCount");
35     private static final AtomicLongFieldUpdater<ConnectorStatisticsImpl> processingTimeUpdater = AtomicLongFieldUpdater.newUpdater(ConnectorStatisticsImpl.class"processingTime");
36     private static final AtomicLongFieldUpdater<ConnectorStatisticsImpl> maxProcessingTimeUpdater = AtomicLongFieldUpdater.newUpdater(ConnectorStatisticsImpl.class"maxProcessingTime");
37     private static final AtomicLongFieldUpdater<ConnectorStatisticsImpl> activeConnectionsUpdater = AtomicLongFieldUpdater.newUpdater(ConnectorStatisticsImpl.class"activeConnections");
38     private static final AtomicLongFieldUpdater<ConnectorStatisticsImpl> maxActiveConnectionsUpdater = AtomicLongFieldUpdater.newUpdater(ConnectorStatisticsImpl.class"maxActiveConnections");
39     private static final AtomicLongFieldUpdater<ConnectorStatisticsImpl> activeRequestsUpdater = AtomicLongFieldUpdater.newUpdater(ConnectorStatisticsImpl.class"activeRequests");
40     private static final AtomicLongFieldUpdater<ConnectorStatisticsImpl> maxActiveRequestsUpdater = AtomicLongFieldUpdater.newUpdater(ConnectorStatisticsImpl.class"maxActiveRequests");
41
42     private volatile long requestCount;
43     private volatile long bytesSent;
44     private volatile long bytesReceived;
45     private volatile long errorCount;
46     private volatile long processingTime;
47     private volatile long maxProcessingTime;
48     private volatile long activeConnections;
49     private volatile long maxActiveConnections;
50     private volatile long activeRequests;
51     private volatile long maxActiveRequests;
52
53     private final ExchangeCompletionListener completionListener = new ExchangeCompletionListener() {
54         @Override
55         public void exchangeEvent(HttpServerExchange exchange, NextListener nextListener) {
56             try {
57                 activeRequestsUpdater.decrementAndGet(ConnectorStatisticsImpl.this);
58                 if (exchange.getStatusCode() == StatusCodes.INTERNAL_SERVER_ERROR) {
59                     errorCountUpdater.incrementAndGet(ConnectorStatisticsImpl.this);
60                 }
61                 long start = exchange.getRequestStartTime();
62                 if (start > 0) {
63                     long elapsed = System.nanoTime() - start;
64                     processingTimeUpdater.addAndGet(ConnectorStatisticsImpl.this, elapsed);
65                     long oldMax;
66                     do {
67                         oldMax = maxProcessingTimeUpdater.get(ConnectorStatisticsImpl.this);
68                         if (oldMax >= elapsed) {
69                             break;
70                         }
71                     } while (!maxProcessingTimeUpdater.compareAndSet(ConnectorStatisticsImpl.this, oldMax, elapsed));
72                 }
73
74             } finally {
75                 nextListener.proceed();
76             }
77         }
78     };
79
80     private final ByteActivityCallback bytesSentAccumulator = new BytesSentAccumulator();
81     private final ByteActivityCallback bytesReceivedAccumulator = new BytesReceivedAccumulator();
82
83     @Override
84     public long getRequestCount() {
85         return requestCountUpdater.get(this);
86     }
87
88     @Override
89     public long getBytesSent() {
90         return bytesSentUpdater.get(this);
91     }
92
93     @Override
94     public long getBytesReceived() {
95         return bytesReceivedUpdater.get(this);
96     }
97
98     @Override
99     public long getErrorCount() {
100         return errorCountUpdater.get(this);
101     }
102
103     @Override
104     public long getProcessingTime() {
105         return processingTimeUpdater.get(this);
106     }
107
108     @Override
109     public long getMaxProcessingTime() {
110         return maxProcessingTimeUpdater.get(this);
111     }
112
113     @Override
114     public void reset() {
115         requestCountUpdater.set(this, 0);
116         bytesSentUpdater.set(this, 0);
117         bytesReceivedUpdater.set(this, 0);
118         errorCountUpdater.set(this, 0);
119         maxProcessingTimeUpdater.set(this, 0);
120         processingTimeUpdater.set(this, 0);
121         maxActiveConnectionsUpdater.set(this, 0);
122         maxActiveRequestsUpdater.set(this, 0);
123         //we don't update active requests or connections, as these will still be live
124     }
125
126     public void requestFinished(long bytesSent, long bytesReceived, boolean error) {
127         bytesSentUpdater.addAndGet(this, bytesSent);
128         bytesReceivedUpdater.addAndGet(this, bytesReceived);
129         if (error) {
130             errorCountUpdater.incrementAndGet(this);
131         }
132     }
133
134     public void updateBytesSent(long bytes) {
135         bytesSentUpdater.addAndGet(this, bytes);
136     }
137
138     public void updateBytesReceived(long bytes) {
139         bytesReceivedUpdater.addAndGet(this, bytes);
140     }
141
142     public void setup(HttpServerExchange exchange) {
143         requestCountUpdater.incrementAndGet(this);
144         long current = activeRequestsUpdater.incrementAndGet(this);
145         long maxActiveRequests;
146         do {
147             maxActiveRequests = this.maxActiveRequests;
148             if(current <= maxActiveRequests) {
149                 break;
150             }
151         } while (!maxActiveRequestsUpdater.compareAndSet(this, maxActiveRequests, current));
152         exchange.addExchangeCompleteListener(completionListener);
153     }
154
155     public ByteActivityCallback sentAccumulator() {
156         return bytesSentAccumulator;
157     }
158
159     public ByteActivityCallback receivedAccumulator() {
160         return bytesReceivedAccumulator;
161     }
162
163     //todo: we can do a way
164     private class BytesSentAccumulator implements ByteActivityCallback {
165         @Override
166         public void activity(long bytes) {
167             bytesSentUpdater.addAndGet(ConnectorStatisticsImpl.this, bytes);
168         }
169     }
170
171     private class BytesReceivedAccumulator implements ByteActivityCallback {
172         @Override
173         public void activity(long bytes) {
174             bytesReceivedUpdater.addAndGet(ConnectorStatisticsImpl.this, bytes);
175         }
176     }
177
178     @Override
179     public long getActiveConnections() {
180         return activeConnections;
181     }
182
183     @Override
184     public long getMaxActiveConnections() {
185         return maxActiveConnections;
186     }
187
188     public void incrementConnectionCount() {
189         long current = activeConnectionsUpdater.incrementAndGet(this);
190         long maxActiveConnections;
191         do {
192             maxActiveConnections = this.maxActiveConnections;
193             if(current <= maxActiveConnections) {
194                 return;
195             }
196         } while (!maxActiveConnectionsUpdater.compareAndSet(this, maxActiveConnections, current));
197     }
198
199     public void decrementConnectionCount() {
200         activeConnectionsUpdater.decrementAndGet(this);
201     }
202     @Override
203     public long getActiveRequests() {
204         return activeRequests;
205     }
206
207     @Override
208     public long getMaxActiveRequests() {
209         return maxActiveRequests;
210     }
211 }
212