View Javadoc

1   /**
2    *  Copyright 2003-2006 Greg Luck
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  
18  package net.sf.ehcache;
19  
20  import org.apache.commons.logging.Log;
21  import org.apache.commons.logging.LogFactory;
22  
23  import java.io.ByteArrayInputStream;
24  import java.io.ByteArrayOutputStream;
25  import java.io.IOException;
26  import java.io.ObjectInputStream;
27  import java.io.ObjectOutputStream;
28  import java.io.Serializable;
29  
30  /**
31   * A Cache Element, consisting of a key, value and attributes. 
32   * <p/>
33   * From ehcache-1.2, Elements can have keys and values that are Serializable or Objects. To preserve backward
34   * compatibility, special accessor methods for Object keys and values are provided: {@link #getObjectKey()} and
35   * {@link #getObjectValue()}. If placing Objects in ehcace, developers must use the new getObject... methods to
36   * avoid CacheExceptions. The get... methods are reserved for Serializable keys and values.
37   *
38   * @author Greg Luck
39   * @version $Id: Element.java 52 2006-04-24 14:50:03Z gregluck $
40   * @noinspection SerializableHasSerializationMethods
41   */
42  public final class Element implements Serializable, Cloneable {
43      /**
44       * serial version
45       * Updated version 1.2
46       */
47      private static final long serialVersionUID = 7832456720941087574L;
48  
49      private static final Log LOG = LogFactory.getLog(Element.class.getName());
50  
51  
52      /**
53       * the cache key.
54       */
55      private final Object key;
56  
57      /**
58       * the value.
59       */
60      private final Object value;
61  
62      /**
63       * version of the element.
64       */
65      private long version;
66  
67      /**
68       * The creation time.
69       */
70      private long creationTime;
71  
72      /**
73       * The last access time.
74       */
75      private long lastAccessTime;
76  
77      /**
78       * The next to last access time. Used by the expiry mechanism
79       */
80      private long nextToLastAccessTime;
81  
82      /**
83       * The number of times the element was hit.
84       */
85      private long hitCount;
86  
87      /**
88       * A full constructor.
89       * <p/>
90       * Creation time is set to the current time. Last Access Time and Previous To Last Access Time
91       * are not set.
92       * @since .4
93       */
94      public Element(Serializable key, Serializable value, long version) {
95          this((Object) key, (Object) value, version);
96  
97      }
98  
99      /**
100      * A full constructor.
101      * <p/>
102      * Creation time is set to the current time. Last Access Time and Previous To Last Access Time
103      * are not set.
104      * @since 1.2
105      */
106     public Element(Object key, Object value, long version) {
107         this.key = key;
108         this.value = value;
109         this.version = version;
110         creationTime = System.currentTimeMillis();
111         hitCount = 0;
112     }
113 
114     /**
115      * Constructor.
116      *
117      * @param key
118      * @param value
119      */
120     public Element(Serializable key, Serializable value) {
121         this((Object)key, (Object)value, 1L);
122     }
123 
124     /**
125      * Constructor.
126      *
127      * @param key
128      * @param value
129      * @since 1.2
130      */
131     public Element(Object key, Object value) {
132         this(key, value, 1L);
133     }
134 
135     /**
136      * Gets the key attribute of the Element object.
137      *
138      * @return The key value. If the key is not Serializable, null is returned and an info log message emitted
139      * @see #getObjectKey()
140      */
141     public final Serializable getKey() {
142         Serializable keyAsSerializable;
143         try {
144             keyAsSerializable = (Serializable) key;
145         } catch (Exception e) {
146             throw new CacheException("Key " + key + " is not Serializable. Consider using Element#getObjectKey()");
147         }
148         return keyAsSerializable;
149     }
150 
151     /**
152      * Gets the key attribute of the Element object.
153      * <p/>
154      * This method is provided for those wishing to use ehcache as a memory only cache
155      * and enables retrieval of non-Serializable values from elements.
156      *
157      * @return The key as an Object. i.e no restriction is placed on it
158      * @see #getKey()
159      */
160     public final Object getObjectKey() {
161         return key;
162     }
163 
164     /**
165      * Gets the value attribute of the Element object.
166      *
167      * @return The value which must be Serializable. If not use {@link #getObjectValue}. If the value is not Serializable, null is returned and an info log message emitted
168      * @see #getObjectValue()
169      */
170     public final Serializable getValue() {
171         Serializable valueAsSerializable;
172         try {
173             valueAsSerializable = (Serializable) value;
174         } catch (Exception e) {
175             throw new CacheException("Value " + value + " is not Serializable. Consider using Element#getObjectKey()");
176         }
177         return valueAsSerializable;
178     }
179 
180     /**
181      * Gets the value attribute of the Element object as an Object.
182      * <p/>
183      * This method is provided for those wishing to use ehcache as a memory only cache
184      * and enables retrieval of non-Serializable values from elements.
185      *
186      * @return The value as an Object.  i.e no restriction is placed on it
187      * @see #getValue()
188      * @since 1.2
189      */
190     public final Object getObjectValue() {
191         return value;
192     }
193 
194     /**
195      * Equals comparison with another element, based on the key.
196      */
197     public final boolean equals(Object object) {
198         if (object == null) {
199             return false;
200         }
201 
202         Element element = (Element) object;
203         if (key == null || element.getObjectKey() == null) {
204             return false;
205         }
206 
207         return key.equals(element.getObjectKey());
208     }
209 
210     /**
211      * Gets the hascode, based on the key.
212      */
213     public final int hashCode() {
214         return key.hashCode();
215     }
216 
217     /**
218      * Sets the version attribute of the ElementAttributes object.
219      *
220      * @param version The new version value
221      */
222     public final void setVersion(long version) {
223         this.version = version;
224     }
225 
226     /**
227      * Gets the creationTime attribute of the ElementAttributes object.
228      *
229      * @return The creationTime value
230      */
231     public final long getCreationTime() {
232         return creationTime;
233     }
234 
235     /**
236      * Sets the creationTime attribute of the ElementAttributes object.
237      */
238     public final void setCreateTime() {
239         creationTime = System.currentTimeMillis();
240     }
241 
242     /**
243      * Gets the version attribute of the ElementAttributes object.
244      *
245      * @return The version value
246      */
247     public final long getVersion() {
248         return version;
249     }
250 
251     /**
252      * Gets the last access time.
253      * Access means a get. So a newly created {@link Element}
254      * will have a last access time equal to its create time.
255      */
256     public final long getLastAccessTime() {
257         return lastAccessTime;
258     }
259 
260     /**
261      * Gets the next to last access time. This is package protected as it should
262      * not be used outside internal Cache housekeeping.
263      *
264      * @see #getLastAccessTime()
265      */
266     final long getNextToLastAccessTime() {
267         return nextToLastAccessTime;
268     }
269 
270     /**
271      * Gets the hit count on this element.
272      */
273     public final long getHitCount() {
274         return hitCount;
275     }
276 
277     /**
278      * Resets the hit count to 0 and the last access time to 0.
279      */
280     public final void resetAccessStatistics() {
281         lastAccessTime = 0;
282         nextToLastAccessTime = 0;
283         hitCount = 0;
284     }
285 
286     /**
287      * Sets the last access time to now.
288      */
289     public final void updateAccessStatistics() {
290         nextToLastAccessTime = lastAccessTime;
291         lastAccessTime = System.currentTimeMillis();
292         hitCount++;
293     }
294 
295     /**
296      * Returns a {@link String} representation of the {@link Element}.
297      */
298     public final String toString() {
299         StringBuffer sb = new StringBuffer();
300 
301         sb.append("[ key = ").append(key)
302                 .append(", value=").append(value)
303                 .append(", version=").append(version)
304                 .append(", hitCount=").append(hitCount)
305                 .append(", CreationTime = ").append(this.getCreationTime())
306                 .append(", LastAccessTime = ").append(this.getLastAccessTime())
307                 .append(" ]");
308 
309         return sb.toString();
310     }
311 
312     /**
313      * Clones an Element. A completely new object is created, with no common references with the
314      * existing one.
315      * <p/>
316      * This method will not work unless the Object is Serializable
317      * <p/>
318      * Warning: This can be very slow on large object graphs. If you use this method
319      * you should write a performance test to verify suitability.
320      * @return a new {@link Element}, with exactly the same field values as the one it was cloned from.
321      * @throws CloneNotSupportedException
322      */
323     public final Object clone() throws CloneNotSupportedException {
324         //Not used. Just to get code inspectors to shut up
325         super.clone();
326 
327         Element element = new Element(deepCopy(key), deepCopy(value), version);
328         element.creationTime = creationTime;
329         element.lastAccessTime = lastAccessTime;
330         element.nextToLastAccessTime = nextToLastAccessTime;
331         element.hitCount = hitCount;
332         return element;
333     }
334 
335     private Object deepCopy(Object oldValue) {
336         Serializable newValue = null;
337         ByteArrayOutputStream bout = new ByteArrayOutputStream();
338         ObjectOutputStream oos = null;
339         ObjectInputStream ois = null;
340         try {
341             oos = new ObjectOutputStream(bout);
342             oos.writeObject(oldValue);
343             ByteArrayInputStream bin = new ByteArrayInputStream(bout.toByteArray());
344             ois = new ObjectInputStream(bin);
345             newValue = (Serializable) ois.readObject();
346         } catch (IOException e) {
347             LOG.error("Error cloning Element with key " + key
348                     + " during serialization and deserialization of value");
349         } catch (ClassNotFoundException e) {
350             LOG.error("Error cloning Element with key " + key
351                     + " during serialization and deserialization of value");
352         } finally {
353             try {
354                 if (oos != null) {
355                     oos.close();
356                 }
357                 if (ois != null) {
358                     ois.close();
359                 }
360             } catch (Exception e) {
361                 LOG.error("Error closing Stream");
362             }
363         }
364         return newValue;
365     }
366 
367     /**
368      * The size of this object in serialized form. This is not the same
369      * thing as the memory size, which is JVM dependent. Relative values should be meaningful,
370      * however.
371      * <p/>
372      * Warning: This method can be <b>very slow</b> for values which contain large object graphs.
373      *
374      * @return The serialized size in bytes
375      */
376     public final long getSerializedSize() {
377         if (!isSerializable()) {
378             return 0;
379         }
380         long size = 0;
381         ByteArrayOutputStream bout = new ByteArrayOutputStream();
382         ObjectOutputStream oos = null;
383         try {
384             oos = new ObjectOutputStream(bout);
385             oos.writeObject(this);
386             size = bout.size();
387             return size;
388         } catch (IOException e) {
389             LOG.error("Error measuring element size for element with key " + key);
390         } finally {
391             try {
392                 if (oos != null) {
393                     oos.close();
394                 }
395             } catch (Exception e) {
396                 LOG.error("Error closing ObjectOutputStream");
397             }
398         }
399 
400         return size;
401     }
402 
403     /**
404      * Whether the element may be Serialized.
405      * <p/>
406      * While Element implements Serializable, it is possible to create non Serializable elements
407      * for use in MemoryStores. This method checks that an instance of Element really is Serializable
408      * and will not throw a NonSerializableException if Serialized.
409      * @return true if the element is Serializable
410      * @since 1.2
411      */
412     public final boolean isSerializable() {
413         return key instanceof Serializable && value instanceof Serializable;
414     }
415 
416     /**
417      * Whether the element's key may be Serialized.
418      * <p/>
419      * While Element implements Serializable, it is possible to create non Serializable elements and/or
420      * non Serializable keys for use in MemoryStores.
421      * <p/>
422      * This method checks that an instance of an Element's key really is Serializable
423      * and will not throw a NonSerializableException if Serialized.
424      * @return true if the element's key is Serializable
425      * @since 1.2
426      */
427     public final boolean isKeySerializable() {
428         return key instanceof Serializable;
429 }
430         }
431 
432 
433