Upstream version 10.39.225.0
[platform/framework/web/crosswalk.git] / src / third_party / cacheinvalidation / src / javaexample / com / google / ipc / invalidation / examples / android2 / ExampleListenerState.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 package com.google.ipc.invalidation.examples.android2;
17
18 import com.google.ipc.invalidation.examples.android2.ExampleListenerProto.ExampleListenerStateProto;
19 import com.google.ipc.invalidation.examples.android2.ExampleListenerProto.ExampleListenerStateProto.ObjectIdProto;
20 import com.google.ipc.invalidation.examples.android2.ExampleListenerProto.ExampleListenerStateProto.ObjectStateProto;
21 import com.google.ipc.invalidation.external.client.types.ObjectId;
22 import com.google.protobuf.nano.InvalidProtocolBufferNanoException;
23 import com.google.protobuf.nano.MessageNano;
24
25 import android.util.Base64;
26 import android.util.Log;
27
28 import java.util.ArrayList;
29 import java.util.Date;
30 import java.util.HashMap;
31 import java.util.List;
32 import java.util.Locale;
33 import java.util.Map;
34
35
36 /**
37  * Wrapper around persistent state for {@link ExampleListener}.
38  *
39  */
40 public class ExampleListenerState {
41
42   /** Wrapper around persistent state for an object tracked by the {@link ExampleListener}. */
43   private static class ObjectState {
44     /** Object id for the object being tracked. */
45     final ObjectId objectId;
46
47     /** Indicates whether the example listener wants to be registered for this object. */
48     boolean isRegistered;
49
50     /**
51      * Payload of the invalidation with the highest version received so far. {@code null} before
52      * any invalidations have been received or after an unknown-version invalidation is received.
53      */
54     byte[] payload;
55
56     /**
57      * Highest version invalidation received so far. {@code null} before any invalidations have
58      * been received or after an unknown-version invalidation is received.
59      */
60     Long highestVersion;
61
62     /** Wall time in milliseconds at which most recent invalidation was received. */
63     Long invalidationTimeMillis;
64
65     /** Indicates whether the last invalidation received was a background invalidation. */
66     boolean isBackground;
67
68     ObjectState(ObjectStateProto objectStateProto) {
69       objectId = deserializeObjectId(objectStateProto.objectId);
70       isRegistered = objectStateProto.isRegistered;
71       payload = objectStateProto.payload;
72       highestVersion = objectStateProto.highestVersion;
73       invalidationTimeMillis = objectStateProto.invalidationTimeMillis;
74       isBackground = objectStateProto.isBackground;
75     }
76
77     ObjectState(ObjectId objectId, boolean isRegistered) {
78       this.objectId = objectId;
79       this.isRegistered = isRegistered;
80     }
81
82     ObjectStateProto serialize() {
83       ObjectStateProto proto = new ObjectStateProto();
84       proto.objectId = serializeObjectId(objectId);
85       proto.isRegistered = isRegistered;
86       proto.isBackground = isBackground;
87       proto.payload = payload;
88       proto.highestVersion = highestVersion;
89       proto.invalidationTimeMillis = invalidationTimeMillis;
90       return proto;
91     }
92
93     @Override
94     public String toString() {
95       StringBuilder builder = new StringBuilder();
96       toString(builder);
97       return builder.toString();
98     }
99
100     void toString(StringBuilder builder) {
101       builder.append(isRegistered ? "REG " : "UNREG ").append(objectId);
102       if (payload != null) {
103         builder.append(", |payload|=").append(payload.length);
104       }
105       if (highestVersion != null) {
106         builder.append(", highestVersion=").append(highestVersion.longValue());
107       }
108       if (isBackground) {
109         builder.append(", isBackground");
110       }
111       if (invalidationTimeMillis != null) {
112         builder.append(", invalidationTime=").append(new Date(invalidationTimeMillis.longValue()));
113       }
114     }
115   }
116
117   /** The tag used for logging in the listener state class. */
118   private static final String TAG = "TEA2:ELS";
119
120   /** Number of objects we're interested in tracking by default. */
121   
122   static final int NUM_INTERESTING_OBJECTS = 4;
123
124   /** Object source for objects the client is initially tracking. */
125   private static final int DEMO_SOURCE = 4;
126
127   /** Prefix for object names the client is initially tracking. */
128   private static final String OBJECT_ID_PREFIX = "Obj";
129
130   /** State for all tracked objects. */
131   private final Map<ObjectId, ObjectState> trackedObjects;
132
133   /** Client id reported by {@code AndroidListener#ready} call. */
134   private byte[] clientId;
135
136   private ExampleListenerState(Map<ObjectId, ObjectState> trackedObjects,
137       byte[] clientId) {
138     if (trackedObjects == null) {
139       throw new NullPointerException();
140     }
141     this.trackedObjects = trackedObjects;
142     this.clientId = clientId;
143   }
144
145   public static ExampleListenerState deserialize(String data) {
146     HashMap<ObjectId, ObjectState> trackedObjects = new HashMap<ObjectId, ObjectState>();
147     byte[] clientId;
148     ExampleListenerStateProto stateProto = tryParseStateProto(data);
149     if (stateProto == null) {
150       // By default, we're interested in objects with ids Obj1, Obj2, ...
151       for (int i = 1; i <= NUM_INTERESTING_OBJECTS; ++i) {
152         ObjectId objectId = ObjectId.newInstance(DEMO_SOURCE, (OBJECT_ID_PREFIX + i).getBytes());
153         trackedObjects.put(objectId, new ObjectState(objectId, true));
154       }
155       clientId = null;
156     } else {
157       // Load interesting objects from state proto.
158       for (ObjectStateProto objectStateProto : stateProto.objectState) {
159         ObjectState objectState = new ObjectState(objectStateProto);
160         trackedObjects.put(objectState.objectId, objectState);
161       }
162       clientId = stateProto.clientId;
163     }
164     return new ExampleListenerState(trackedObjects, clientId);
165   }
166
167   /** Returns proto serialized in data or null if it cannot be decoded. */
168   private static ExampleListenerStateProto tryParseStateProto(String data) {
169     if (data == null) {
170       return null;
171     }
172     final byte[] bytes;
173     try {
174       bytes = Base64.decode(data, Base64.DEFAULT);
175     } catch (IllegalArgumentException exception) {
176       Log.e(TAG, String.format(Locale.ROOT, "Illegal base 64 encoding. data='%s', error='%s'", data,
177           exception.getMessage()));
178       return null;
179     }
180     try {
181       ExampleListenerStateProto proto =
182           MessageNano.mergeFrom(new ExampleListenerStateProto(), bytes);
183       return proto;
184     } catch (InvalidProtocolBufferNanoException exception) {
185       Log.e(TAG, String.format(Locale.ROOT, "Error parsing state bytes. data='%s', error='%s'",
186           data, exception.getMessage()));
187       return null;
188     }
189   }
190
191   /** Serializes example listener state to string. */
192   public String serialize() {
193     ExampleListenerStateProto proto = new ExampleListenerStateProto();
194     proto.objectState = new ObjectStateProto[trackedObjects.size()];
195     int index = 0;
196     for (ObjectState objectState : trackedObjects.values()) {
197       ObjectStateProto objectStateProto = objectState.serialize();
198       proto.objectState[index++] = objectStateProto;
199     }
200     proto.clientId = clientId;
201     return Base64.encodeToString(MessageNano.toByteArray(proto), Base64.DEFAULT);
202   }
203
204   Iterable<ObjectId> getInterestingObjects() {
205     List<ObjectId> interestingObjects = new ArrayList<ObjectId>(trackedObjects.size());
206     for (ObjectState objectState : trackedObjects.values()) {
207       if (objectState.isRegistered) {
208         interestingObjects.add(objectState.objectId);
209       }
210     }
211     return interestingObjects;
212   }
213
214   byte[] getClientId() {
215     return clientId;
216   }
217
218   /** Sets the client id passed to the example listener via the {@code ready()} call. */
219   void setClientId(byte[] value) {
220     clientId = value;
221   }
222
223   /**
224    * Returns {@code true} if the state indicates a registration should exist for the given object.
225    */
226   boolean isInterestedInObject(ObjectId objectId) {
227     ObjectState objectState = trackedObjects.get(objectId);
228     return (objectState != null) && objectState.isRegistered;
229   }
230
231   /** Updates state for the given object to indicate it should be registered. */
232   boolean addObjectOfInterest(ObjectId objectId) {
233     ObjectState objectState = trackedObjects.get(objectId);
234     if (objectState == null) {
235       objectState = new ObjectState(objectId, true);
236       trackedObjects.put(objectId, objectState);
237       return true;
238     }
239
240     if (objectState.isRegistered) {
241       return false;
242     }
243     objectState.isRegistered = true;
244     return true;
245   }
246
247   /** Updates state for the given object to indicate it should not be registered. */
248   boolean removeObjectOfInterest(ObjectId objectId) {
249     ObjectState objectState = trackedObjects.get(objectId);
250     if (objectState == null) {
251       return false;
252     }
253
254     if (objectState.isRegistered) {
255       objectState.isRegistered = false;
256       return true;
257     }
258     return false;
259   }
260
261   /** Updates state for an object after an unknown-version invalidation is received. */
262   void informUnknownVersionInvalidation(ObjectId objectId) {
263     ObjectState objectState = getObjectStateForInvalidation(objectId);
264     objectState.invalidationTimeMillis = System.currentTimeMillis();
265     objectState.highestVersion = null;
266     objectState.payload = null;
267   }
268
269   /** Updates state for an object after an invalidation is received. */
270   void informInvalidation(ObjectId objectId, long version, byte[] payload,
271       boolean isBackground) {
272     ObjectState objectState = getObjectStateForInvalidation(objectId);
273     if (objectState.highestVersion == null || objectState.highestVersion.longValue() < version) {
274       objectState.highestVersion = version;
275       objectState.payload = payload;
276       objectState.invalidationTimeMillis = System.currentTimeMillis();
277       objectState.isBackground = isBackground;
278     }
279   }
280
281   /**
282    * Updates state when an invalidate all request is received (unknown version is marked for all
283    * objects).
284    */
285   public void informInvalidateAll() {
286     for (ObjectState objectState : trackedObjects.values()) {
287       informUnknownVersionInvalidation(objectState.objectId);
288     }
289   }
290
291   /** Returns existing object state for an object or updates state. */
292   private ObjectState getObjectStateForInvalidation(ObjectId objectId) {
293     ObjectState objectState = trackedObjects.get(objectId);
294     if (objectState == null) {
295       // Invalidation for unregistered object.
296       objectState = new ObjectState(objectId, false);
297       trackedObjects.put(objectId, objectState);
298     }
299     return objectState;
300   }
301
302   /** Returns an object given its serialized form. */
303   static ObjectId deserializeObjectId(ObjectIdProto objectIdProto) {
304     return ObjectId.newInstance(objectIdProto.source, objectIdProto.name);
305   }
306
307   /** Serializes the given object id. */
308   static ObjectIdProto serializeObjectId(ObjectId objectId) {
309     ObjectIdProto proto = new ObjectIdProto();
310     proto.source = objectId.getSource();
311     proto.name = objectId.getName();
312     return proto;
313   }
314
315   /** Clears all state for the example listener. */
316   void clear() {
317     trackedObjects.clear();
318     clientId = null;
319   }
320
321   @Override
322   public String toString() {
323     StringBuilder builder = new StringBuilder();
324     if (clientId != null) {
325       builder.append("ready!\n");
326     }
327     for (ObjectState objectState : trackedObjects.values()) {
328       objectState.toString(builder);
329       builder.append("\n");
330     }
331     return builder.toString();
332   }
333 }