Upstream version 10.39.225.0
[platform/framework/web/crosswalk.git] / src / third_party / cacheinvalidation / src / java / com / google / ipc / invalidation / ticl / Statistics.java
1 /*
2  * Copyright 2011 Google Inc.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16
17 package com.google.ipc.invalidation.ticl;
18
19 import com.google.ipc.invalidation.external.client.SystemResources.Logger;
20 import com.google.ipc.invalidation.external.client.types.SimplePair;
21 import com.google.ipc.invalidation.ticl.proto.ClientProtocol.PropertyRecord;
22 import com.google.ipc.invalidation.ticl.proto.JavaClient.StatisticsState;
23 import com.google.ipc.invalidation.util.InternalBase;
24 import com.google.ipc.invalidation.util.Marshallable;
25 import com.google.ipc.invalidation.util.TextBuilder;
26 import com.google.ipc.invalidation.util.TypedUtil;
27
28 import java.util.ArrayList;
29 import java.util.Collection;
30 import java.util.HashMap;
31 import java.util.List;
32 import java.util.Map;
33
34 /**
35  * Statistics for the Ticl, e.g., number of registration calls, number of token mismatches, etc.
36  *
37  */
38 public class Statistics extends InternalBase implements Marshallable<StatisticsState> {
39
40   // Implementation: To classify the statistics a bit better, we have a few enums to track different
41   // types of statistics, e.g., sent message types, errors, etc. For each statistic type, we create
42   // a map and provide a method to record an event for each type of statistic.
43
44   /** Types of messages sent to the server: {@code ClientToServerMessage} for their description. */
45   public enum SentMessageType {
46     INFO,
47     INITIALIZE,
48     INVALIDATION_ACK,
49     REGISTRATION,
50     REGISTRATION_SYNC,
51     TOTAL,  // Refers to the actual ClientToServerMessage message sent on the network.
52   }
53
54   /**
55    * Types of messages received from the server: {@code ServerToClientMessage} for their
56    * description.
57    */
58   public enum ReceivedMessageType {
59     INFO_REQUEST,
60     INVALIDATION,
61     REGISTRATION_STATUS,
62     REGISTRATION_SYNC_REQUEST,
63     TOKEN_CONTROL,
64     ERROR,
65     CONFIG_CHANGE,
66     STALE_INVALIDATION,  // An already acked INVALIDATION.
67     TOTAL, // Refers to the actual ServerToClientMessage messages received from the network.
68   }
69
70   /** Interesting API calls coming from the application ({@code InvalidationClient}). */
71   public enum IncomingOperationType {
72     ACKNOWLEDGE,
73     REGISTRATION,
74     UNREGISTRATION,
75   }
76
77   /** Different types of events issued by the {@code InvalidationListener}). */
78   public enum ListenerEventType {
79     INFORM_ERROR,
80     INFORM_REGISTRATION_FAILURE,
81     INFORM_REGISTRATION_STATUS,
82     INVALIDATE,
83     INVALIDATE_ALL,
84     INVALIDATE_UNKNOWN,
85     REISSUE_REGISTRATIONS,
86   }
87
88   /** Different types of errors observed by the Ticl. */
89   public enum ClientErrorType {
90     /** Acknowledge call received from client with a bad handle. */
91     ACKNOWLEDGE_HANDLE_FAILURE,
92
93     /** Incoming message dropped due to parsing, validation problems. */
94     INCOMING_MESSAGE_FAILURE,
95
96     /** Tried to send an outgoing message that was invalid. */
97     OUTGOING_MESSAGE_FAILURE,
98
99     /** Persistent state failed to deserialize correctly. */
100     PERSISTENT_DESERIALIZATION_FAILURE,
101
102     /** Read of blob from persistent state failed. */
103     PERSISTENT_READ_FAILURE,
104
105     /** Write of blob from persistent state failed. */
106     PERSISTENT_WRITE_FAILURE,
107
108     /** Message received with incompatible protocol version. */
109     PROTOCOL_VERSION_FAILURE,
110
111     /**
112      * Registration at client and server is different, e.g., client thinks it is registered while
113      * the server says it is unregistered (of course, sync will fix it).
114      */
115     REGISTRATION_DISCREPANCY,
116
117     /** The nonce from the server did not match the current nonce by the client. */
118     NONCE_MISMATCH,
119
120     /** The current token at the client is different from the token in the incoming message. */
121     TOKEN_MISMATCH,
122
123     /** No message sent due to token missing. */
124     TOKEN_MISSING_FAILURE,
125
126     /** Received a message with a token (transient) failure. */
127     TOKEN_TRANSIENT_FAILURE,
128   }
129
130   // Names of statistics types. Do not rely on reflection to determine type names because Proguard
131   // may change them for Android clients.
132   private static final String SENT_MESSAGE_TYPE_NAME = "SentMessageType";
133   private static final String INCOMING_OPERATION_TYPE_NAME = "IncomingOperationType";
134   private static final String RECEIVED_MESSAGE_TYPE_NAME = "ReceivedMessageType";
135   private static final String LISTENER_EVENT_TYPE_NAME = "ListenerEventType";
136   private static final String CLIENT_ERROR_TYPE_NAME = "ClientErrorType";
137
138   // Map from stats enum names to values. Used in place of Enum.valueOf() because this method
139   // invokes Enum.values() via reflection, and that method may be renamed by Proguard.
140   private static final Map<String, SentMessageType> SENT_MESSAGE_TYPE_NAME_TO_VALUE_MAP =
141       createValueOfMap(SentMessageType.values());
142   private static final Map<String, IncomingOperationType>
143       INCOMING_OPERATION_TYPE_NAME_TO_VALUE_MAP = createValueOfMap(IncomingOperationType.values());
144   private static final Map<String, ReceivedMessageType> RECEIVED_MESSAGE_TYPE_NAME_TO_VALUE_MAP =
145       createValueOfMap(ReceivedMessageType.values());
146   private static final Map<String, ListenerEventType> LISTENER_EVENT_TYPE_NAME_TO_VALUE_MAP =
147       createValueOfMap(ListenerEventType.values());
148   private static final Map<String, ClientErrorType> CLIENT_ERROR_TYPE_NAME_TO_VALUE_MAP =
149       createValueOfMap(ClientErrorType.values());
150
151   // Maps for each type of Statistic to keep track of how many times each event has occurred.
152
153   private final Map<SentMessageType, Integer> sentMessageTypes =
154       new HashMap<SentMessageType, Integer>();
155   private final Map<ReceivedMessageType, Integer> receivedMessageTypes =
156       new HashMap<ReceivedMessageType, Integer>();
157   private final Map<IncomingOperationType, Integer> incomingOperationTypes =
158       new HashMap<IncomingOperationType, Integer>();
159   private final Map<ListenerEventType, Integer> listenerEventTypes =
160       new HashMap<ListenerEventType, Integer>();
161   private final Map<ClientErrorType, Integer> clientErrorTypes =
162       new HashMap<ClientErrorType, Integer>();
163
164   public Statistics() {
165     initializeMap(sentMessageTypes, SentMessageType.values());
166     initializeMap(receivedMessageTypes, ReceivedMessageType.values());
167     initializeMap(incomingOperationTypes, IncomingOperationType.values());
168     initializeMap(listenerEventTypes, ListenerEventType.values());
169     initializeMap(clientErrorTypes, ClientErrorType.values());
170   }
171
172   /** Returns a copy of this. */
173   public Statistics getCopyForTest() {
174     Statistics statistics = new Statistics();
175     statistics.sentMessageTypes.putAll(sentMessageTypes);
176     statistics.receivedMessageTypes.putAll(receivedMessageTypes);
177     statistics.incomingOperationTypes.putAll(incomingOperationTypes);
178     statistics.listenerEventTypes.putAll(listenerEventTypes);
179     statistics.clientErrorTypes.putAll(clientErrorTypes);
180     return statistics;
181   }
182
183   /** Returns the counter value for {@code clientErrorType}. */
184   int getClientErrorCounterForTest(ClientErrorType clientErrorType) {
185     return TypedUtil.mapGet(clientErrorTypes, clientErrorType);
186   }
187
188   /** Returns the counter value for {@code sentMessageType}. */
189   int getSentMessageCounterForTest(SentMessageType sentMessageType) {
190     return TypedUtil.mapGet(sentMessageTypes, sentMessageType);
191   }
192
193   /** Returns the counter value for {@code receivedMessageType}. */
194   int getReceivedMessageCounterForTest(ReceivedMessageType receivedMessageType) {
195     return TypedUtil.mapGet(receivedMessageTypes, receivedMessageType);
196   }
197
198   /** Records the fact that a message of type {@code sentMessageType} has been sent. */
199   public void recordSentMessage(SentMessageType sentMessageType) {
200     incrementValue(sentMessageTypes, sentMessageType);
201   }
202
203   /** Records the fact that a message of type {@code receivedMessageType} has been received. */
204   public void recordReceivedMessage(ReceivedMessageType receivedMessageType) {
205     incrementValue(receivedMessageTypes, receivedMessageType);
206   }
207
208   /**
209    * Records the fact that the application has made a call of type
210    * {@code incomingOperationType}.
211    */
212   public void recordIncomingOperation(IncomingOperationType incomingOperationType) {
213     incrementValue(incomingOperationTypes, incomingOperationType);
214   }
215
216   /** Records the fact that the listener has issued an event of type {@code listenerEventType}. */
217   public void recordListenerEvent(ListenerEventType listenerEventType) {
218     incrementValue(listenerEventTypes, listenerEventType);
219   }
220
221   /** Records the fact that the client has observed an error of type {@code clientErrorType}. */
222   public void recordError(ClientErrorType clientErrorType) {
223     incrementValue(clientErrorTypes, clientErrorType);
224   }
225
226   /**
227    * Modifies {@code performanceCounters} to contain all the statistics that are non-zero. Each pair
228    * has the name of the statistic event and the number of times that event has occurred since the
229    * client started.
230    */
231   public void getNonZeroStatistics(List<SimplePair<String, Integer>> performanceCounters) {
232     // Add the non-zero values from the different maps to performanceCounters.
233     fillWithNonZeroStatistics(sentMessageTypes, performanceCounters, SENT_MESSAGE_TYPE_NAME);
234     fillWithNonZeroStatistics(receivedMessageTypes, performanceCounters,
235         RECEIVED_MESSAGE_TYPE_NAME);
236     fillWithNonZeroStatistics(incomingOperationTypes, performanceCounters,
237         INCOMING_OPERATION_TYPE_NAME);
238     fillWithNonZeroStatistics(listenerEventTypes, performanceCounters, LISTENER_EVENT_TYPE_NAME);
239     fillWithNonZeroStatistics(clientErrorTypes, performanceCounters, CLIENT_ERROR_TYPE_NAME);
240   }
241
242   /** Modifies {@code result} to contain those statistics from {@code map} whose value is > 0. */
243   private static <Key extends Enum<Key>> void fillWithNonZeroStatistics(Map<Key, Integer> map,
244       List<SimplePair<String, Integer>> destination, String typeName) {
245     String prefix = typeName + ".";
246     for (Map.Entry<Key, Integer> entry : map.entrySet()) {
247       if (entry.getValue() > 0) {
248         destination.add(SimplePair.of(prefix + entry.getKey().name(), entry.getValue()));
249       }
250     }
251   }
252
253   /** Initializes a map from enum names to values of the given {@code keys}. */
254   private static <Key extends Enum<Key>> Map<String, Key> createValueOfMap(Key[] keys) {
255     HashMap<String, Key> map = new HashMap<String, Key>();
256     for (Key key : keys) {
257       map.put(key.name(), key);
258     }
259     return map;
260   }
261
262   /** Increments the value of {@code map}[{@code key}] by 1. */
263   private static <Key> void incrementValue(Map<Key, Integer> map, Key key) {
264     map.put(key, TypedUtil.mapGet(map, key) + 1);
265   }
266
267   /** Initializes all values for {@code keys} in {@code map} to be 0. */
268   private static <Key> void initializeMap(Map<Key, Integer> map, Key[] keys) {
269     for (Key key : keys) {
270       map.put(key, 0);
271     }
272   }
273
274   @Override
275   public void toCompactString(TextBuilder builder) {
276     List<SimplePair<String, Integer>> nonZeroValues = new ArrayList<SimplePair<String, Integer>>();
277     getNonZeroStatistics(nonZeroValues);
278     builder.appendFormat("Client Statistics: %s\n", nonZeroValues);
279   }
280
281   @Override
282   public StatisticsState marshal() {
283     // Get all the non-zero counters, convert them to proto PropertyRecord messages, and return
284     // a StatisticsState containing the records.
285     List<SimplePair<String, Integer>> counters = new ArrayList<SimplePair<String, Integer>>();
286     getNonZeroStatistics(counters);
287     List<PropertyRecord> propertyRecords = new ArrayList<PropertyRecord>(counters.size());
288     for (SimplePair<String, Integer> counter : counters) {
289       propertyRecords.add(PropertyRecord.create(counter.getFirst(), counter.getSecond()));
290     }
291     return StatisticsState.create(propertyRecords);
292   }
293
294   /**
295    * Given the serialized {@code performanceCounters} of the client statistics, returns a Statistics
296    * object with the performance counter values from {@code performanceCounters}.
297    */
298   
299   public static Statistics deserializeStatistics(Logger logger,
300       Collection<PropertyRecord> performanceCounters) {
301     Statistics statistics = new Statistics();
302
303     // For each counter, parse out the counter name and value.
304     for (PropertyRecord performanceCounter : performanceCounters) {
305       String counterName = performanceCounter.getName();
306       String[] parts = counterName.split("\\.");
307       if (parts.length != 2) {
308         logger.warning("Perf counter name must of form: class.value, skipping: %s", counterName);
309         continue;
310       }
311       String className = parts[0];
312       String fieldName = parts[1];
313       int counterValue = performanceCounter.getValue();
314
315       // Call the relevant method in a loop (i.e., depending on the type of the class).
316       if (TypedUtil.<String>equals(className, SENT_MESSAGE_TYPE_NAME)) {
317         incrementPerformanceCounterValue(logger, SENT_MESSAGE_TYPE_NAME_TO_VALUE_MAP,
318             statistics.sentMessageTypes, fieldName, counterValue);
319       } else if (TypedUtil.<String>equals(className, INCOMING_OPERATION_TYPE_NAME)) {
320         incrementPerformanceCounterValue(logger, INCOMING_OPERATION_TYPE_NAME_TO_VALUE_MAP,
321             statistics.incomingOperationTypes, fieldName, counterValue);
322       } else if (TypedUtil.<String>equals(className, RECEIVED_MESSAGE_TYPE_NAME)) {
323         incrementPerformanceCounterValue(logger, RECEIVED_MESSAGE_TYPE_NAME_TO_VALUE_MAP,
324             statistics.receivedMessageTypes, fieldName, counterValue);
325       } else if (TypedUtil.<String>equals(className,  LISTENER_EVENT_TYPE_NAME)) {
326         incrementPerformanceCounterValue(logger, LISTENER_EVENT_TYPE_NAME_TO_VALUE_MAP,
327             statistics.listenerEventTypes, fieldName, counterValue);
328       } else if (TypedUtil.<String>equals(className,  CLIENT_ERROR_TYPE_NAME)) {
329         incrementPerformanceCounterValue(logger, CLIENT_ERROR_TYPE_NAME_TO_VALUE_MAP,
330             statistics.clientErrorTypes, fieldName, counterValue);
331       } else {
332         logger.warning("Skipping unknown enum class name %s", className);
333       }
334     }
335     return statistics;
336   }
337
338   /**
339    * Looks for an enum value with the given {@code fieldName} in {@code valueOfMap} and increments
340    * the corresponding entry in {@code counts} by {@code counterValue}. Call to update statistics
341    * for a single performance counter.
342    */
343   private static <Key extends Enum<Key>> void incrementPerformanceCounterValue(Logger logger,
344       Map<String, Key> valueOfMap, Map<Key, Integer> counts, String fieldName, int counterValue) {
345     Key type = TypedUtil.mapGet(valueOfMap, fieldName);
346     if (type != null) {
347       int currentValue = TypedUtil.mapGet(counts, type);
348       counts.put(type, currentValue + counterValue);
349     } else {
350       logger.warning("Skipping unknown enum value name %s", fieldName);
351     }
352   }
353 }