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
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