1 /**
2  *  Copyright Terracotta, 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
18 package net.sf.ehcache;
19
20
21 import net.sf.ehcache.config.CacheConfiguration;
22 import net.sf.ehcache.pool.sizeof.annotations.IgnoreSizeOf;
23 import net.sf.ehcache.util.TimeUtil;
24
25 import java.io.ByteArrayInputStream;
26 import java.io.ByteArrayOutputStream;
27 import java.io.IOException;
28 import java.io.NotSerializableException;
29 import java.io.ObjectInputStream;
30 import java.io.ObjectOutputStream;
31 import java.io.Serializable;
32 import java.util.concurrent.atomic.AtomicLongFieldUpdater;
33
34 import org.slf4j.Logger;
35 import org.slf4j.LoggerFactory;
36
37 /**
38  * A Cache Element, consisting of a key, value and attributes.
39  * <p/>
40  * From ehcache-1.2, Elements can have keys and values that are Serializable or Objects. To preserve backward
41  * compatibility, special accessor methods for Object keys and values are provided: {@link #getObjectKey()} and
42  * {@link #getObjectValue()}. If placing Objects in ehcache, developers must use the new getObject... methods to
43  * avoid CacheExceptions. The get... methods are reserved for Serializable keys and values.
44  *
45  * @author Greg Luck
46  * @version $Id: Element.java 6224 2012-09-04 14:13:29Z cdennis $
47  */

48 public class Element implements Serializable, Cloneable {
49
50     /**
51      * serial version
52      * Updated for version 1.2, 1.2.1 and 1.7
53      */

54     private static final long serialVersionUID = 1098572221246444544L;
55
56     private static final Logger LOG = LoggerFactory.getLogger(Element.class.getName());
57
58     private static final AtomicLongFieldUpdater<Element> HIT_COUNT_UPDATER = AtomicLongFieldUpdater.newUpdater(Element.class"hitCount");
59
60     private static final boolean ELEMENT_VERSION_AUTO = Boolean.getBoolean("net.sf.ehcache.element.version.auto");
61
62     private static final long NOT_SET_ID = 0;
63
64     static {
65         if (ELEMENT_VERSION_AUTO) {
66             LOG.warn("Note that net.sf.ehcache.element.version.auto is set and user provided version will not be honored");
67         }
68     }
69
70     /**
71      * the cache key.
72      */

73     @IgnoreSizeOf
74     private final Object key;
75
76     /**
77      * the value.
78      */

79     private final Object value;
80
81     /**
82      * version of the element. System.currentTimeMillis() is used to compute version for updated elements. That
83      * way, the actual version of the updated element does not need to be checked.
84      */

85     private volatile long version;
86
87     /**
88      * The number of times the element was hit.
89      */

90     private volatile long hitCount;
91
92     /**
93      * The amount of time for the element to live, in seconds. 0 indicates unlimited.
94      */

95     private volatile int timeToLive = Integer.MIN_VALUE;
96
97     /**
98      * The amount of time for the element to idle, in seconds. 0 indicates unlimited.
99      */

100     private volatile int timeToIdle = Integer.MIN_VALUE;
101
102     /**
103      * Pluggable element eviction data instance
104      */

105     private transient volatile ElementEvictionData elementEvictionData;
106
107     /**
108      * If there is an Element in the Cache and it is replaced with a new Element for the same key,
109      * then both the version number and lastUpdateTime should be updated to reflect that. The creation time
110      * will be the creation time of the new Element, not the original one, so that TTL concepts still work.
111      */

112     private volatile long lastUpdateTime;
113
114     private volatile boolean cacheDefaultLifespan = true;
115
116     private volatile long id = NOT_SET_ID;
117
118     /**
119      * A full constructor.
120      * <p/>
121      * Creation time is set to the current time. Last Access Time is not set.
122      *
123      * @since .4
124      */

125     public Element(final Serializable key, final Serializable value, final long version) {
126         this((Object) key, (Object) value, version);
127
128     }
129
130     /**
131      * A full constructor.
132      * <p/>
133      * Creation time is set to the current time. Last Access Time and Previous To Last Access Time
134      * are not set.
135      *
136      * @since 1.2
137      */

138     public Element(final Object key, final Object value, final long version) {
139         this.key = key;
140         this.value = value;
141         this.version = version;
142         HIT_COUNT_UPDATER.set(this, 0);
143         this.elementEvictionData = new DefaultElementEvictionData(System.currentTimeMillis());
144     }
145
146     /**
147      * Constructor.
148      *
149      * @deprecated The {@code nextToLastAccessTime} field is unused since
150      *             version 1.7, setting it will have no effect. Use
151      *             #Element(Object, Object, longlonglonglonglong)
152      *             instead
153      * @since 1.3
154      * @see #Element(Object, Object, longlonglonglonglong)
155      */

156     @Deprecated
157     public Element(final Object key, final Object value, final long version,
158                    final long creationTime, final long lastAccessTime, final long nextToLastAccessTime,
159                    final long lastUpdateTime, final long hitCount) {
160         this(key, value, version, creationTime, lastAccessTime, lastUpdateTime, hitCount);
161     }
162
163     /**
164      * Constructor.
165      *
166      * @since 1.7
167      */

168     public Element(final Object key, final Object value, final long version,
169                    final long creationTime, final long lastAccessTime,
170                    final long lastUpdateTime, final long hitCount) {
171         this.key = key;
172         this.value = value;
173         this.version = version;
174         this.lastUpdateTime = lastUpdateTime;
175         HIT_COUNT_UPDATER.set(this, hitCount);
176         this.elementEvictionData = new DefaultElementEvictionData(creationTime, lastAccessTime);
177     }
178
179     /**
180      * Constructor used by ElementData. Needs to be public since ElementData might be in another classloader
181      *
182      * @since 1.7
183      */

184     public Element(final Object key, final Object value, final long version, final long creationTime,
185             final long lastAccessTime, final long hitCount, final boolean cacheDefaultLifespan,
186             final int timeToLive, final int timeToIdle, final long lastUpdateTime) {
187         this.key = key;
188         this.value = value;
189         this.version = version;
190         HIT_COUNT_UPDATER.set(this, hitCount);
191         this.cacheDefaultLifespan = cacheDefaultLifespan;
192         this.timeToLive = timeToLive;
193         this.timeToIdle = timeToIdle;
194         this.lastUpdateTime = lastUpdateTime;
195         this.elementEvictionData = new DefaultElementEvictionData(creationTime, lastAccessTime);
196     }
197
198     /**
199      * Constructor used by ehcache-server
200      *
201      * @param key               any non null value
202      * @param value             any value, including nulls
203      * @param eternal           specify as non-null to override cache configuration
204      * @param timeToIdleSeconds specify as non-null to override cache configuration
205      * @param timeToLiveSeconds specify as non-null to override cache configuration
206      */

207     public Element(final Object key, final Object value,
208                    final Boolean eternal, final Integer timeToIdleSeconds, final Integer timeToLiveSeconds) {
209         this.key = key;
210         this.value = value;
211         if (eternal != null) {
212             setEternal(eternal.booleanValue());
213         }
214         if (timeToIdleSeconds != null) {
215             setTimeToIdle(timeToIdleSeconds.intValue());
216         }
217         if (timeToLiveSeconds != null) {
218             setTimeToLive(timeToLiveSeconds.intValue());
219         }
220         this.elementEvictionData = new DefaultElementEvictionData(System.currentTimeMillis());
221     }
222
223     /**
224      * Constructor.
225      *
226      * @param key
227      * @param value
228      */

229     public Element(final Serializable key, final Serializable value) {
230         this((Object) key, (Object) value, 1L);
231     }
232
233     /**
234      * Constructor.
235      *
236      * @param key
237      * @param value
238      * @since 1.2
239      */

240     public Element(final Object key, final Object value) {
241         this(key, value, 1L);
242     }
243
244     /**
245      * Gets the key attribute of the Element object.
246      *
247      * @return The key value.
248      * @throws CacheException if the key is not {@code Serializable}.
249      * @deprecated Please use {@link #getObjectKey()} instead.
250      */

251     @Deprecated
252     public final Serializable getKey() throws CacheException {
253         try {
254             return (Serializable) getObjectKey();
255         } catch (ClassCastException e) {
256             throw new CacheException("The key " + getObjectKey() + " is not Serializable. Consider using Element.getObjectKey()");
257         }
258     }
259
260     /**
261      * Gets the key attribute of the Element object.
262      * <p/>
263      * This method is provided for those wishing to use ehcache as a memory only cache
264      * and enables retrieval of non-Serializable values from elements.
265      *
266      * @return The key as an Object. i.e no restriction is placed on it
267      * @see #getKey()
268      */

269     public final Object getObjectKey() {
270         return key;
271     }
272
273     /**
274      * Gets the value attribute of the Element object.
275      *
276      * @return The value which must be {@code Serializable}. If not use {@link #getObjectValue}.
277      * @throws CacheException if the value is not {@code Serializable}.
278      * @deprecated Please use {@link #getObjectValue()} instead.
279      */

280     @Deprecated
281     public final Serializable getValue() throws CacheException {
282         try {
283             return (Serializable) getObjectValue();
284         } catch (ClassCastException e) {
285             throw new CacheException("The value " + getObjectValue() + for key " + getObjectKey() +
286                     " is not Serializable. Consider using Element.getObjectValue()");
287         }
288     }
289
290     /**
291      * Gets the value attribute of the Element object as an Object.
292      * <p/>
293      * This method is provided for those wishing to use ehcache as a memory only cache
294      * and enables retrieval of non-Serializable values from elements.
295      *
296      * @return The value as an Object.  i.e no restriction is placed on it
297      * @see #getValue()
298      * @since 1.2
299      */

300     public final Object getObjectValue() {
301         return value;
302     }
303
304     /**
305      * Equals comparison with another element, based on the key.
306      */

307     @Override
308     public final boolean equals(final Object object) {
309         if (object == null || !(object instanceof Element)) {
310             return false;
311         }
312
313         Element element = (Element) object;
314         if (key == null || element.getObjectKey() == null) {
315             return false;
316         }
317
318         return key.equals(element.getObjectKey());
319     }
320
321     /**
322      * Sets time to Live
323      *
324      * @param timeToLiveSeconds the number of seconds to live
325      */

326     public void setTimeToLive(final int timeToLiveSeconds) {
327         if (timeToLiveSeconds < 0) {
328             throw new IllegalArgumentException("timeToLive can't be negative");
329         }
330         this.cacheDefaultLifespan = false;
331         this.timeToLive = timeToLiveSeconds;
332     }
333
334     /**
335      * Sets time to idle
336      *
337      * @param timeToIdleSeconds the number of seconds to idle
338      */

339     public void setTimeToIdle(final int timeToIdleSeconds) {
340         if (timeToIdleSeconds < 0) {
341             throw new IllegalArgumentException("timeToIdle can't be negative");
342         }
343         this.cacheDefaultLifespan = false;
344         this.timeToIdle = timeToIdleSeconds;
345     }
346
347     /**
348      * Gets the hashcode, based on the key.
349      */

350     @Override
351     public final int hashCode() {
352         return key.hashCode();
353     }
354
355     /**
356      * Sets the version attribute of the ElementAttributes object.
357      *
358      * @param version The new version value
359      */

360     public final void setVersion(final long version) {
361         this.version = version;
362     }
363
364     /**
365      * Sets the element identifier (this field is used internally by ehcache). Setting this field in application code will not be preserved
366      *
367      * @param id The new id value
368      */

369     void setId(final long id) {
370         if (id == NOT_SET_ID) {
371             throw new IllegalArgumentException("Id cannot be set to " + id);
372         }
373         this.id = id;
374     }
375
376     /**
377      * Gets the element identifier (this field is used internally by ehcache)
378      *
379      * @return id the id
380      */

381     long getId() {
382         final long v = id;
383         if (v == NOT_SET_ID) {
384             throw new IllegalStateException("Id not set");
385         }
386         return v;
387     }
388
389     /**
390      * Determines if an Id has been set on this element
391      *
392      * @return true if this element has an Id
393      */

394     boolean hasId() {
395         return id != NOT_SET_ID;
396     }
397
398     /**
399      * Sets the creationTime attribute of the ElementAttributes object.
400      * <p>
401      * Note that in a Terracotta clustered environment, resetting the creation
402      * time will not have any effect.
403      *
404      * @deprecated Resetting the creation time is not recommended as of version
405      *             1.7
406      */

407     @Deprecated
408     public final void setCreateTime() {
409         this.elementEvictionData.setCreationTime(System.currentTimeMillis());
410     }
411
412     /**
413      * Gets the creationTime of the Element
414      *
415      * @return The creationTime value
416      */

417     public final long getCreationTime() {
418         return elementEvictionData.getCreationTime();
419     }
420
421     /**
422      * Calculates the latest of creation and update time
423      * @return if never updated, creation time is returned, otherwise updated time
424      */

425     public final long getLatestOfCreationAndUpdateTime() {
426         if (0 == lastUpdateTime) {
427             return elementEvictionData.getCreationTime();
428         } else {
429             return lastUpdateTime;
430         }
431     }
432
433     /**
434      * Gets the version attribute of the ElementAttributes object.
435      *
436      * @return The version value
437      */

438     public final long getVersion() {
439         return version;
440     }
441
442     /**
443      * Gets the last access time.
444      * Access means a get. So a newly created {@link Element}
445      * will have a last access time equal to its create time.
446      */

447     public final long getLastAccessTime() {
448         return elementEvictionData.getLastAccessTime();
449     }
450
451     /**
452      * Gets the next to last access time.
453      *
454      * @deprecated The {@code nextToLastAccessTime} field is unused since
455      *             version 1.7, retrieving it will return the {@code
456      *             lastAccessTime}. Use #getLastAccessTime() instead.
457      * @see #getLastAccessTime()
458      */

459     @Deprecated
460     public final long getNextToLastAccessTime() {
461         return getLastAccessTime();
462     }
463
464     /**
465      * Gets the hit count on this element.
466      */

467     public final long getHitCount() {
468         return hitCount;
469     }
470
471     /**
472      * Retrieves this element's eviction data instance.
473      *
474      * @return this element's eviction data instance
475      */

476     public ElementEvictionData getElementEvictionData() {
477         return elementEvictionData;
478     }
479
480     /**
481      * Sets this element's eviction data instance.
482      *
483      * @param elementEvictionData this element's eviction data
484      */

485     public void setElementEvictionData(ElementEvictionData elementEvictionData) {
486         this.elementEvictionData = elementEvictionData;
487     }
488
489     /**
490      * Resets the hit count to 0 and the last access time to now. Used when an Element is put into a cache.
491      */

492     public final void resetAccessStatistics() {
493         elementEvictionData.resetLastAccessTime(this);
494         HIT_COUNT_UPDATER.set(this, 0);
495     }
496
497     /**
498      * Sets the last access time to now and increase the hit count.
499      */

500     public final void updateAccessStatistics() {
501         elementEvictionData.updateLastAccessTime(System.currentTimeMillis(), this);
502         HIT_COUNT_UPDATER.incrementAndGet(this);
503     }
504
505     /**
506      * Sets the last access time to now without updating the hit count.
507      */

508     public final void updateUpdateStatistics() {
509         lastUpdateTime = System.currentTimeMillis();
510         if (ELEMENT_VERSION_AUTO) {
511           version = lastUpdateTime;
512         }
513     }
514
515
516     /**
517      * Returns a {@link String} representation of the {@link Element}.
518      */

519     @Override
520     public final String toString() {
521         StringBuilder sb = new StringBuilder();
522
523         sb.append("[ key = ").append(key)
524                 .append(", value=").append(value)
525                 .append(", version=").append(version)
526                 .append(", hitCount=").append(hitCount)
527                 .append(", CreationTime = ").append(this.getCreationTime())
528                 .append(", LastAccessTime = ").append(this.getLastAccessTime())
529                 .append(" ]");
530
531         return sb.toString();
532     }
533
534     /**
535      * Clones an Element. A completely new object is created, with no common references with the
536      * existing one.
537      * <p/>
538      * This method will not work unless the Object is Serializable
539      * <p/>
540      * Warning: This can be very slow on large object graphs. If you use this method
541      * you should write a performance test to verify suitability.
542      *
543      * @return a new {@link Element}, with exactly the same field values as the one it was cloned from.
544      * @throws CloneNotSupportedException
545      */

546     @Override
547     public final Object clone() throws CloneNotSupportedException {
548         //Not used. Just to get code inspectors to shut up
549         super.clone();
550
551         try {
552             Element element = new Element(deepCopy(key), deepCopy(value), version);
553             element.elementEvictionData = elementEvictionData.clone();
554             HIT_COUNT_UPDATER.set(element, hitCount);
555             return element;
556         } catch (IOException e) {
557             LOG.error("Error cloning Element with key " + key
558                     + " during serialization and deserialization of value");
559             throw new CloneNotSupportedException();
560         } catch (ClassNotFoundException e) {
561             LOG.error("Error cloning Element with key " + key
562                     + " during serialization and deserialization of value");
563             throw new CloneNotSupportedException();
564         }
565     }
566
567     private static Object deepCopy(final Object oldValue) throws IOException, ClassNotFoundException {
568         Serializable newValue = null;
569         ByteArrayOutputStream bout = new ByteArrayOutputStream();
570         ObjectOutputStream oos = null;
571         ObjectInputStream ois = null;
572         try {
573             oos = new ObjectOutputStream(bout);
574             oos.writeObject(oldValue);
575             ByteArrayInputStream bin = new ByteArrayInputStream(bout.toByteArray());
576             ois = new ObjectInputStream(bin);
577             newValue = (Serializable) ois.readObject();
578         } finally {
579             try {
580                 if (oos != null) {
581                     oos.close();
582                 }
583                 if (ois != null) {
584                     ois.close();
585                 }
586             } catch (Exception e) {
587                 LOG.error("Error closing Stream");
588             }
589         }
590         return newValue;
591     }
592
593     /**
594      * The size of this object in serialized form. This is not the same
595      * thing as the memory size, which is JVM dependent. Relative values should be meaningful,
596      * however.
597      * <p/>
598      * Warning: This method can be <b>very slow</b> for values which contain large object graphs.
599      * <p/>
600      * If the key or value of the Element is not Serializable, an error will be logged and 0 will be returned.
601      *
602      * @return The serialized size in bytes
603      */

604     public final long getSerializedSize() {
605
606         if (!isSerializable()) {
607             return 0;
608         }
609         long size = 0;
610         ByteArrayOutputStream bout = new ByteArrayOutputStream();
611         ObjectOutputStream oos = null;
612         try {
613             oos = new ObjectOutputStream(bout);
614             oos.writeObject(this);
615             size = bout.size();
616             return size;
617         } catch (IOException e) {
618             LOG.debug("Error measuring element size for element with key " + key + ". Cause was: " + e.getMessage());
619         } finally {
620             try {
621                 if (oos != null) {
622                     oos.close();
623                 }
624             } catch (Exception e) {
625                 LOG.error("Error closing ObjectOutputStream");
626             }
627         }
628
629         return size;
630     }
631
632     /**
633      * Whether the element may be Serialized.
634      * <p/>
635      * While Element implements Serializable, it is possible to create non Serializable elements
636      * for use in MemoryStores. This method checks that an instance of Element really is Serializable
637      * and will not throw a NonSerializableException if Serialized.
638      * <p/>
639      * This method was tweaked in 1.6 as it has been shown that Serializable classes can be serializaed as can
640      * null, regardless of what class it is a null of. ObjectOutputStream.write(null) works and ObjectInputStream.read()
641      * will read null back.
642      *
643      * @return true if the element is Serializable
644      * @since 1.2
645      */

646     public final boolean isSerializable() {
647         return isKeySerializable()
648             && (value instanceof Serializable || value == null)
649             && elementEvictionData.canParticipateInSerialization();
650     }
651
652     /**
653      * Whether the element's key may be Serialized.
654      * <p/>
655      * While Element implements Serializable, it is possible to create non Serializable elements and/or
656      * non Serializable keys for use in MemoryStores.
657      * <p/>
658      * This method checks that an instance of an Element's key really is Serializable
659      * and will not throw a NonSerializableException if Serialized.
660      *
661      * @return true if the element's key is Serializable
662      * @since 1.2
663      */

664     public final boolean isKeySerializable() {
665         return key instanceof Serializable || key == null;
666     }
667
668     /**
669      * If there is an Element in the Cache and it is replaced with a new Element for the same key,
670      * then both the version number and lastUpdateTime should be updated to reflect that. The creation time
671      * will be the creation time of the new Element, not the original one, so that TTL concepts still work.
672      *
673      * @return the time when the last update occured. If this is the original Element, the time will be null
674      */

675     public long getLastUpdateTime() {
676         return lastUpdateTime;
677     }
678
679     /**
680      * An element is expired if the expiration time as given by {@link #getExpirationTime()} is in the past.
681      *
682      * @return true if the Element is expired, otherwise false. If no lifespan has been set for the Element it is
683      *         considered not able to expire.
684      * @see #getExpirationTime()
685      */

686     public boolean isExpired() {
687         if (!isLifespanSet() || isEternal()) {
688             return false;
689         }
690
691         long now = System.currentTimeMillis();
692         long expirationTime = getExpirationTime();
693
694         return now > expirationTime;
695     }
696
697     /**
698      * An element is expired if the expiration time as given by {@link #getExpirationTime()} is in the past.
699      * <p>
700      * This method in addition propogates the default TTI/TTL values of the supplied cache into this element.
701      *
702      * @param config config to take default parameters from
703      * @return true if the Element is expired, otherwise false. If no lifespan has been set for the Element it is
704      *         considered not able to expire.
705      * @see #getExpirationTime()
706      */

707     public boolean isExpired(CacheConfiguration config) {
708         if (cacheDefaultLifespan) {
709             if (config.isEternal()) {
710                 timeToIdle = 0;
711                 timeToLive = 0;
712             } else {
713                 timeToIdle = TimeUtil.convertTimeToInt(config.getTimeToIdleSeconds());
714                 timeToLive = TimeUtil.convertTimeToInt(config.getTimeToLiveSeconds());
715             }
716         }
717         return isExpired();
718     }
719
720     /**
721      * Returns the expiration time based on time to live. If this element also has a time to idle setting, the expiry
722      * time will vary depending on whether the element is accessed.
723      *
724      * @return the time to expiration
725      */

726     public long getExpirationTime() {
727         if (!isLifespanSet() || isEternal()) {
728             return Long.MAX_VALUE;
729         }
730
731         long expirationTime = 0;
732         long ttlExpiry = elementEvictionData.getCreationTime() + TimeUtil.toMillis(getTimeToLive());
733
734         long mostRecentTime = Math.max(elementEvictionData.getCreationTime(), elementEvictionData.getLastAccessTime());
735         long ttiExpiry = mostRecentTime + TimeUtil.toMillis(getTimeToIdle());
736
737         if (getTimeToLive() != 0 && (getTimeToIdle() == 0 || elementEvictionData.getLastAccessTime() == 0)) {
738             expirationTime = ttlExpiry;
739         } else if (getTimeToLive() == 0) {
740             expirationTime = ttiExpiry;
741         } else {
742             expirationTime = Math.min(ttlExpiry, ttiExpiry);
743         }
744         return expirationTime;
745     }
746
747     /**
748      * @return true if the element is eternal
749      */

750     public boolean isEternal() {
751         return (0 == timeToIdle) && (0 == timeToLive);
752     }
753
754     /**
755      * Sets whether the element is eternal.
756      *
757      * @param eternal
758      */

759     public void setEternal(final boolean eternal) {
760         if (eternal) {
761             this.cacheDefaultLifespan = false;
762             this.timeToIdle = 0;
763             this.timeToLive = 0;
764         } else if (isEternal()) {
765             this.cacheDefaultLifespan = false;
766             this.timeToIdle = Integer.MIN_VALUE;
767             this.timeToLive = Integer.MIN_VALUE;
768         }
769     }
770
771     /**
772      * Whether any combination of eternal, TTL or TTI has been set.
773      *
774      * @return true if set.
775      */

776     public boolean isLifespanSet() {
777         return this.timeToIdle != Integer.MIN_VALUE || this.timeToLive != Integer.MIN_VALUE;
778     }
779
780     /**
781      * @return the time to live, in seconds
782      */

783     public int getTimeToLive() {
784         if (Integer.MIN_VALUE == timeToLive) {
785             return 0;
786         } else {
787             return timeToLive;
788         }
789     }
790
791     /**
792      * @return the time to idle, in seconds
793      */

794     public int getTimeToIdle() {
795         if (Integer.MIN_VALUE == timeToIdle) {
796             return 0;
797         } else {
798             return timeToIdle;
799         }
800     }
801
802     /**
803      * @return <code>false</code> if this Element has a custom lifespan
804      */

805     public boolean usesCacheDefaultLifespan() {
806         return cacheDefaultLifespan;
807     }
808
809     /**
810      * Set the default parameters of this element - those from its enclosing cache.
811      * @param tti TTI in seconds
812      * @param ttl TTL in seconds
813      * @param eternal <code>true</code> if the element is eternal.
814      */

815     protected void setLifespanDefaults(int tti, int ttl, boolean eternal) {
816         if (eternal) {
817             this.timeToIdle = 0;
818             this.timeToLive = 0;
819         } else if (isEternal()) {
820             this.timeToIdle = Integer.MIN_VALUE;
821             this.timeToLive = Integer.MIN_VALUE;
822         } else {
823             timeToIdle = tti;
824             timeToLive = ttl;
825         }
826     }
827
828     /**
829      * Custom serialization write logic
830      */

831     private void writeObject(ObjectOutputStream out) throws IOException {
832         if (!elementEvictionData.canParticipateInSerialization()) {
833             throw new NotSerializableException();
834         }
835         out.defaultWriteObject();
836         out.writeInt(TimeUtil.toSecs(elementEvictionData.getCreationTime()));
837         out.writeInt(TimeUtil.toSecs(elementEvictionData.getLastAccessTime()));
838     }
839
840     /**
841      * Custom serialization read logic
842      */

843     private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException {
844         in.defaultReadObject();
845         elementEvictionData = new DefaultElementEvictionData(TimeUtil.toMillis(in.readInt()), TimeUtil.toMillis(in.readInt()));
846     }
847 }
848