View Javadoc

1   /***
2    *  Copyright 2003-2010 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.html 13146 2011-08-01 17:12:39Z oletizi $
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      /***
63       * the cache key.
64       */
65      @IgnoreSizeOf
66      private final Object key;
67  
68      /***
69       * the value.
70       */
71      private final Object value;
72  
73      /***
74       * version of the element. System.currentTimeMillis() is used to compute version for updated elements. That
75       * way, the actual version of the updated element does not need to be checked.
76       */
77      private volatile long version;
78  
79      /***
80       * The number of times the element was hit.
81       */
82      private volatile long hitCount;
83  
84      /***
85       * The amount of time for the element to live, in seconds. 0 indicates unlimited.
86       */
87      private volatile int timeToLive = Integer.MIN_VALUE;
88  
89      /***
90       * The amount of time for the element to idle, in seconds. 0 indicates unlimited.
91       */
92      private volatile int timeToIdle = Integer.MIN_VALUE;
93  
94      /***
95       * Pluggable element eviction data instance
96       */
97      private transient volatile ElementEvictionData elementEvictionData;
98  
99      /***
100      * If there is an Element in the Cache and it is replaced with a new Element for the same key,
101      * then both the version number and lastUpdateTime should be updated to reflect that. The creation time
102      * will be the creation time of the new Element, not the original one, so that TTL concepts still work.
103      */
104     private volatile long lastUpdateTime;
105 
106     private volatile boolean cacheDefaultLifespan = true;
107 
108     private volatile boolean pinned;
109 
110     /***
111      * A full constructor.
112      * <p/>
113      * Creation time is set to the current time. Last Access Time is not set.
114      *
115      * @since .4
116      */
117     public Element(final Serializable key, final Serializable value, final long version) {
118         this((Object) key, (Object) value, version);
119 
120     }
121 
122     /***
123      * A full constructor.
124      * <p/>
125      * Creation time is set to the current time. Last Access Time and Previous To Last Access Time
126      * are not set.
127      *
128      * @since 1.2
129      */
130     public Element(final Object key, final Object value, final long version) {
131         this.key = key;
132         this.value = value;
133         this.version = version;
134         HIT_COUNT_UPDATER.set(this, 0);
135         this.elementEvictionData = new DefaultElementEvictionData(System.currentTimeMillis());
136     }
137 
138     /***
139      * Constructor.
140      *
141      * @deprecated The {@code nextToLastAccessTime} field is unused since
142      *             version 1.7, setting it will have no effect. Use
143      *             #Element(Object, Object, long, long, long, long, long)
144      *             instead
145      * @since 1.3
146      * @see #Element(Object, Object, long, long, long, long, long)
147      */
148     @Deprecated
149     public Element(final Object key, final Object value, final long version,
150                    final long creationTime, final long lastAccessTime, final long nextToLastAccessTime,
151                    final long lastUpdateTime, final long hitCount) {
152         this(key, value, version, creationTime, lastAccessTime, lastUpdateTime, hitCount);
153     }
154 
155     /***
156      * Constructor.
157      *
158      * @since 1.7
159      */
160     public Element(final Object key, final Object value, final long version,
161                    final long creationTime, final long lastAccessTime,
162                    final long lastUpdateTime, final long hitCount) {
163         this.key = key;
164         this.value = value;
165         this.version = version;
166         this.lastUpdateTime = lastUpdateTime;
167         HIT_COUNT_UPDATER.set(this, hitCount);
168         this.elementEvictionData = new DefaultElementEvictionData(creationTime, lastAccessTime);
169     }
170 
171     /***
172      * Constructor used by ElementData. Needs to be public since ElementData might be in another classloader
173      *
174      * @since 1.7
175      */
176     public Element(final Object key, final Object value, final long version, final long creationTime,
177             final long lastAccessTime, final long hitCount, final boolean cacheDefaultLifespan,
178             final int timeToLive, final int timeToIdle, final long lastUpdateTime) {
179         this.key = key;
180         this.value = value;
181         this.version = version;
182         HIT_COUNT_UPDATER.set(this, hitCount);
183         this.cacheDefaultLifespan = cacheDefaultLifespan;
184         this.timeToLive = timeToLive;
185         this.timeToIdle = timeToIdle;
186         this.lastUpdateTime = lastUpdateTime;
187         this.elementEvictionData = new DefaultElementEvictionData(creationTime, lastAccessTime);
188     }
189 
190     /***
191      * @since 2.5
192      */
193     public Element(final Object key, final Object value, final long version, final long creationTime,
194             final long lastAccessTime, final long hitCount, final boolean cacheDefaultLifespan,
195             final int timeToLive, final int timeToIdle, final long lastUpdateTime, final boolean pinned) {
196         this.key = key;
197         this.value = value;
198         this.version = version;
199         HIT_COUNT_UPDATER.set(this, hitCount);
200         this.cacheDefaultLifespan = cacheDefaultLifespan;
201         this.timeToLive = timeToLive;
202         this.timeToIdle = timeToIdle;
203         this.lastUpdateTime = lastUpdateTime;
204         this.elementEvictionData = new DefaultElementEvictionData(creationTime, lastAccessTime);
205         this.pinned = pinned;
206     }
207 
208 
209     /***
210      * Constructor used by ehcache-server
211      *
212      * @param key               any non null value
213      * @param value             any value, including nulls
214      * @param eternal           specify as non-null to override cache configuration
215      * @param timeToIdleSeconds specify as non-null to override cache configuration
216      * @param timeToLiveSeconds specify as non-null to override cache configuration
217      */
218     public Element(final Object key, final Object value,
219                    final Boolean eternal, final Integer timeToIdleSeconds, final Integer timeToLiveSeconds) {
220         this.key = key;
221         this.value = value;
222         if (eternal != null) {
223             setEternal(eternal.booleanValue());
224         }
225         if (timeToIdleSeconds != null) {
226             setTimeToIdle(timeToIdleSeconds.intValue());
227         }
228         if (timeToLiveSeconds != null) {
229             setTimeToLive(timeToLiveSeconds.intValue());
230         }
231         this.elementEvictionData = new DefaultElementEvictionData(System.currentTimeMillis());
232     }
233 
234     /***
235      * Constructor.
236      *
237      * @param key
238      * @param value
239      */
240     public Element(final Serializable key, final Serializable value) {
241         this((Object) key, (Object) value, 1L);
242     }
243 
244     /***
245      * Constructor.
246      *
247      * @param key
248      * @param value
249      * @since 1.2
250      */
251     public Element(final Object key, final Object value) {
252         this(key, value, 1L);
253     }
254 
255     /***
256      * Gets the key attribute of the Element object.
257      *
258      * @return The key value. If the key is not Serializable, null is returned and an info log message emitted
259      * @see #getObjectKey()
260      */
261     public final Serializable getKey() {
262         Serializable keyAsSerializable;
263         try {
264             keyAsSerializable = (Serializable) key;
265         } catch (Exception e) {
266             throw new CacheException("The key " + key + " is not Serializable. Consider using Element#getObjectKey()");
267         }
268         return keyAsSerializable;
269     }
270 
271     /***
272      * Gets the key attribute of the Element object.
273      * <p/>
274      * This method is provided for those wishing to use ehcache as a memory only cache
275      * and enables retrieval of non-Serializable values from elements.
276      *
277      * @return The key as an Object. i.e no restriction is placed on it
278      * @see #getKey()
279      */
280     public final Object getObjectKey() {
281         return key;
282     }
283 
284     /***
285      * Gets the value attribute of the Element object.
286      *
287      * @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
288      * @see #getObjectValue()
289      */
290     public final Serializable getValue() {
291         Serializable valueAsSerializable;
292         try {
293             valueAsSerializable = (Serializable) value;
294         } catch (Exception e) {
295             throw new CacheException("The value " + value + " for key " + key +
296                     " is not Serializable. Consider using Element#getObjectValue()");
297         }
298         return valueAsSerializable;
299     }
300 
301     /***
302      * Gets the value attribute of the Element object as an Object.
303      * <p/>
304      * This method is provided for those wishing to use ehcache as a memory only cache
305      * and enables retrieval of non-Serializable values from elements.
306      *
307      * @return The value as an Object.  i.e no restriction is placed on it
308      * @see #getValue()
309      * @since 1.2
310      */
311     public final Object getObjectValue() {
312         return value;
313     }
314 
315     /***
316      * Equals comparison with another element, based on the key.
317      */
318     @Override
319     public final boolean equals(final Object object) {
320         if (object == null || !(object instanceof Element)) {
321             return false;
322         }
323 
324         Element element = (Element) object;
325         if (key == null || element.getObjectKey() == null) {
326             return false;
327         }
328 
329         return key.equals(element.getObjectKey());
330     }
331 
332     /***
333      * Sets time to Live
334      *
335      * @param timeToLiveSeconds the number of seconds to live
336      */
337     public void setTimeToLive(final int timeToLiveSeconds) {
338         if (timeToLiveSeconds < 0) {
339             throw new IllegalArgumentException("timeToLive can't be negative");
340         }
341         this.cacheDefaultLifespan = false;
342         this.timeToLive = timeToLiveSeconds;
343     }
344 
345     /***
346      * Sets time to idle
347      *
348      * @param timeToIdleSeconds the number of seconds to idle
349      */
350     public void setTimeToIdle(final int timeToIdleSeconds) {
351         if (timeToIdleSeconds < 0) {
352             throw new IllegalArgumentException("timeToIdle can't be negative");
353         }
354         this.cacheDefaultLifespan = false;
355         this.timeToIdle = timeToIdleSeconds;
356     }
357 
358     /***
359      * Gets the hashcode, based on the key.
360      */
361     @Override
362     public final int hashCode() {
363         return key.hashCode();
364     }
365 
366     /***
367      * Sets the version attribute of the ElementAttributes object.
368      *
369      * @param version The new version value
370      */
371     public final void setVersion(final long version) {
372         this.version = version;
373     }
374 
375     /***
376      * Sets the creationTime attribute of the ElementAttributes object.
377      * <p>
378      * Note that in a Terracotta clustered environment, resetting the creation
379      * time will not have any effect.
380      *
381      * @deprecated Resetting the creation time is not recommended as of version
382      *             1.7
383      */
384     @Deprecated
385     public final void setCreateTime() {
386         this.elementEvictionData.setCreationTime(System.currentTimeMillis());
387     }
388 
389     /***
390      * Gets the creationTime of the Element
391      *
392      * @return The creationTime value
393      */
394     public final long getCreationTime() {
395         return elementEvictionData.getCreationTime();
396     }
397 
398     /***
399      * Calculates the latest of creation and update time
400      * @return if never updated, creation time is returned, otherwise updated time
401      */
402     public final long getLatestOfCreationAndUpdateTime() {
403         if (0 == lastUpdateTime) {
404             return elementEvictionData.getCreationTime();
405         } else {
406             return lastUpdateTime;
407         }
408     }
409 
410     /***
411      * Gets the version attribute of the ElementAttributes object.
412      *
413      * @return The version value
414      */
415     public final long getVersion() {
416         return version;
417     }
418 
419     /***
420      * Gets the last access time.
421      * Access means a get. So a newly created {@link Element}
422      * will have a last access time equal to its create time.
423      */
424     public final long getLastAccessTime() {
425         return elementEvictionData.getLastAccessTime();
426     }
427 
428     /***
429      * Gets the next to last access time.
430      *
431      * @deprecated The {@code nextToLastAccessTime} field is unused since
432      *             version 1.7, retrieving it will return the {@code
433      *             lastAccessTime}. Use #getLastAccessTime() instead.
434      * @see #getLastAccessTime()
435      */
436     @Deprecated
437     public final long getNextToLastAccessTime() {
438         return getLastAccessTime();
439     }
440 
441     /***
442      * Gets the hit count on this element.
443      */
444     public final long getHitCount() {
445         return hitCount;
446     }
447 
448     /***
449      * Retrieves this element's eviction data instance.
450      *
451      * @return this element's eviction data instance
452      */
453     public ElementEvictionData getElementEvictionData() {
454         return elementEvictionData;
455     }
456 
457     /***
458      * Sets this element's eviction data instance.
459      *
460      * @param elementEvictionData this element's eviction data
461      */
462     public void setElementEvictionData(ElementEvictionData elementEvictionData) {
463         this.elementEvictionData = elementEvictionData;
464     }
465 
466     /***
467      * Resets the hit count to 0 and the last access time to now. Used when an Element is put into a cache.
468      */
469     public final void resetAccessStatistics() {
470         elementEvictionData.resetLastAccessTime(this);
471         HIT_COUNT_UPDATER.set(this, 0);
472     }
473 
474     /***
475      * Sets the last access time to now and increase the hit count.
476      */
477     public final void updateAccessStatistics() {
478         elementEvictionData.updateLastAccessTime(System.currentTimeMillis(), this);
479         HIT_COUNT_UPDATER.incrementAndGet(this);
480     }
481 
482     /***
483      * Sets the last access time to now without updating the hit count.
484      */
485     public final void updateUpdateStatistics() {
486         lastUpdateTime = System.currentTimeMillis();
487         if (ELEMENT_VERSION_AUTO) {
488           version = lastUpdateTime;
489         }
490     }
491 
492 
493     /***
494      * Returns a {@link String} representation of the {@link Element}.
495      */
496     @Override
497     public final String toString() {
498         StringBuilder sb = new StringBuilder();
499 
500         sb.append("[ key = ").append(key)
501                 .append(", value=").append(value)
502                 .append(", version=").append(version)
503                 .append(", hitCount=").append(hitCount)
504                 .append(", CreationTime = ").append(this.getCreationTime())
505                 .append(", LastAccessTime = ").append(this.getLastAccessTime())
506                 .append(" ]");
507 
508         return sb.toString();
509     }
510 
511     /***
512      * Clones an Element. A completely new object is created, with no common references with the
513      * existing one.
514      * <p/>
515      * This method will not work unless the Object is Serializable
516      * <p/>
517      * Warning: This can be very slow on large object graphs. If you use this method
518      * you should write a performance test to verify suitability.
519      *
520      * @return a new {@link Element}, with exactly the same field values as the one it was cloned from.
521      * @throws CloneNotSupportedException
522      */
523     @Override
524     public final Object clone() throws CloneNotSupportedException {
525         //Not used. Just to get code inspectors to shut up
526         super.clone();
527 
528         try {
529             Element element = new Element(deepCopy(key), deepCopy(value), version);
530             element.elementEvictionData = elementEvictionData.clone();
531             HIT_COUNT_UPDATER.set(element, hitCount);
532             return element;
533         } catch (IOException e) {
534             LOG.error("Error cloning Element with key " + key
535                     + " during serialization and deserialization of value");
536             throw new CloneNotSupportedException();
537         } catch (ClassNotFoundException e) {
538             LOG.error("Error cloning Element with key " + key
539                     + " during serialization and deserialization of value");
540             throw new CloneNotSupportedException();
541         }
542     }
543 
544     private static Object deepCopy(final Object oldValue) throws IOException, ClassNotFoundException {
545         Serializable newValue = null;
546         ByteArrayOutputStream bout = new ByteArrayOutputStream();
547         ObjectOutputStream oos = null;
548         ObjectInputStream ois = null;
549         try {
550             oos = new ObjectOutputStream(bout);
551             oos.writeObject(oldValue);
552             ByteArrayInputStream bin = new ByteArrayInputStream(bout.toByteArray());
553             ois = new ObjectInputStream(bin);
554             newValue = (Serializable) ois.readObject();
555         } finally {
556             try {
557                 if (oos != null) {
558                     oos.close();
559                 }
560                 if (ois != null) {
561                     ois.close();
562                 }
563             } catch (Exception e) {
564                 LOG.error("Error closing Stream");
565             }
566         }
567         return newValue;
568     }
569 
570     /***
571      * The size of this object in serialized form. This is not the same
572      * thing as the memory size, which is JVM dependent. Relative values should be meaningful,
573      * however.
574      * <p/>
575      * Warning: This method can be <b>very slow</b> for values which contain large object graphs.
576      * <p/>
577      * If the key or value of the Element is not Serializable, an error will be logged and 0 will be returned.
578      *
579      * @return The serialized size in bytes
580      */
581     public final long getSerializedSize() {
582 
583         if (!isSerializable()) {
584             return 0;
585         }
586         long size = 0;
587         ByteArrayOutputStream bout = new ByteArrayOutputStream();
588         ObjectOutputStream oos = null;
589         try {
590             oos = new ObjectOutputStream(bout);
591             oos.writeObject(this);
592             size = bout.size();
593             return size;
594         } catch (IOException e) {
595             LOG.debug("Error measuring element size for element with key " + key + ". Cause was: " + e.getMessage());
596         } finally {
597             try {
598                 if (oos != null) {
599                     oos.close();
600                 }
601             } catch (Exception e) {
602                 LOG.error("Error closing ObjectOutputStream");
603             }
604         }
605 
606         return size;
607     }
608 
609     /***
610      * Whether the element may be Serialized.
611      * <p/>
612      * While Element implements Serializable, it is possible to create non Serializable elements
613      * for use in MemoryStores. This method checks that an instance of Element really is Serializable
614      * and will not throw a NonSerializableException if Serialized.
615      * <p/>
616      * This method was tweaked in 1.6 as it has been shown that Serializable classes can be serializaed as can
617      * null, regardless of what class it is a null of. ObjectOutputStream.write(null) works and ObjectInputStream.read()
618      * will read null back.
619      *
620      * @return true if the element is Serializable
621      * @since 1.2
622      */
623     public final boolean isSerializable() {
624         return isKeySerializable()
625             && (value instanceof Serializable || value == null)
626             && elementEvictionData.canParticipateInSerialization();
627     }
628 
629     /***
630      * Whether the element's key may be Serialized.
631      * <p/>
632      * While Element implements Serializable, it is possible to create non Serializable elements and/or
633      * non Serializable keys for use in MemoryStores.
634      * <p/>
635      * This method checks that an instance of an Element's key really is Serializable
636      * and will not throw a NonSerializableException if Serialized.
637      *
638      * @return true if the element's key is Serializable
639      * @since 1.2
640      */
641     public final boolean isKeySerializable() {
642         return key instanceof Serializable || key == null;
643     }
644 
645     /***
646      * If there is an Element in the Cache and it is replaced with a new Element for the same key,
647      * then both the version number and lastUpdateTime should be updated to reflect that. The creation time
648      * will be the creation time of the new Element, not the original one, so that TTL concepts still work.
649      *
650      * @return the time when the last update occured. If this is the original Element, the time will be null
651      */
652     public long getLastUpdateTime() {
653         return lastUpdateTime;
654     }
655 
656     /***
657      * An element is expired if the expiration time as given by {@link #getExpirationTime()} is in the past.
658      *
659      * @return true if the Element is expired, otherwise false. If no lifespan has been set for the Element it is
660      *         considered not able to expire.
661      * @see #getExpirationTime()
662      */
663     public boolean isExpired() {
664         if (!isLifespanSet() || isEternal()) {
665             return false;
666         }
667 
668         long now = System.currentTimeMillis();
669         long expirationTime = getExpirationTime();
670 
671         return now > expirationTime;
672     }
673 
674     /***
675      * An element is expired if the expiration time as given by {@link #getExpirationTime()} is in the past.
676      * <p>
677      * This method in addition propogates the default TTI/TTL values of the supplied cache into this element.
678      *
679      * @param config config to take default parameters from
680      * @return true if the Element is expired, otherwise false. If no lifespan has been set for the Element it is
681      *         considered not able to expire.
682      * @see #getExpirationTime()
683      */
684     public boolean isExpired(CacheConfiguration config) {
685         if (cacheDefaultLifespan) {
686             if (config.isEternal()) {
687                 timeToIdle = 0;
688                 timeToLive = 0;
689             } else {
690                 timeToIdle = TimeUtil.convertTimeToInt(config.getTimeToIdleSeconds());
691                 timeToLive = TimeUtil.convertTimeToInt(config.getTimeToLiveSeconds());
692             }
693         }
694         return isExpired();
695     }
696 
697     /***
698      * Returns the expiration time based on time to live. If this element also has a time to idle setting, the expiry
699      * time will vary depending on whether the element is accessed.
700      *
701      * @return the time to expiration
702      */
703     public long getExpirationTime() {
704         if (!isLifespanSet() || isEternal()) {
705             return Long.MAX_VALUE;
706         }
707 
708         long expirationTime = 0;
709         long ttlExpiry = elementEvictionData.getCreationTime() + TimeUtil.toMillis(getTimeToLive());
710 
711         long mostRecentTime = Math.max(elementEvictionData.getCreationTime(), elementEvictionData.getLastAccessTime());
712         long ttiExpiry = mostRecentTime + TimeUtil.toMillis(getTimeToIdle());
713 
714         if (getTimeToLive() != 0 && (getTimeToIdle() == 0 || elementEvictionData.getLastAccessTime() == 0)) {
715             expirationTime = ttlExpiry;
716         } else if (getTimeToLive() == 0) {
717             expirationTime = ttiExpiry;
718         } else {
719             expirationTime = Math.min(ttlExpiry, ttiExpiry);
720         }
721         return expirationTime;
722     }
723 
724     /***
725      * @return true if the element is eternal
726      */
727     public boolean isEternal() {
728         return (0 == timeToIdle) && (0 == timeToLive);
729     }
730 
731     /***
732      * Sets whether the element is eternal.
733      *
734      * @param eternal
735      */
736     public void setEternal(final boolean eternal) {
737         if (eternal) {
738             this.cacheDefaultLifespan = false;
739             this.timeToIdle = 0;
740             this.timeToLive = 0;
741         } else if (isEternal()) {
742             this.cacheDefaultLifespan = false;
743             this.timeToIdle = Integer.MIN_VALUE;
744             this.timeToLive = Integer.MIN_VALUE;
745         }
746     }
747 
748     /***
749      * Whether any combination of eternal, TTL or TTI has been set.
750      *
751      * @return true if set.
752      */
753     public boolean isLifespanSet() {
754         return this.timeToIdle != Integer.MIN_VALUE || this.timeToLive != Integer.MIN_VALUE;
755     }
756 
757     /***
758      * @return the time to live, in seconds
759      */
760     public int getTimeToLive() {
761         if (Integer.MIN_VALUE == timeToLive) {
762             return 0;
763         } else {
764             return timeToLive;
765         }
766     }
767 
768     /***
769      * @return the time to idle, in seconds
770      */
771     public int getTimeToIdle() {
772         if (Integer.MIN_VALUE == timeToIdle) {
773             return 0;
774         } else {
775             return timeToIdle;
776         }
777     }
778 
779     /***
780      * @return <code>false</code> if this Element has a custom lifespan
781      */
782     public boolean usesCacheDefaultLifespan() {
783         return cacheDefaultLifespan;
784     }
785 
786     /***
787      * Mark the element as pinned or not
788      *
789      * @param pinned true if the element should be pinned, false otherwise
790      */
791     public void setPinned(boolean pinned) {
792         this.pinned = pinned;
793     }
794 
795     /***
796      * Check if the element is pinned
797      *
798      * @return true if the element is pinned
799      */
800     public boolean isPinned() {
801         return pinned;
802     }
803 
804     /***
805      * Set the default parameters of this element - those from its enclosing cache.
806      * @param tti TTI in seconds
807      * @param ttl TTL in seconds
808      * @param eternal <code>true</code> if the element is eternal.
809      */
810     protected void setLifespanDefaults(int tti, int ttl, boolean eternal) {
811         if (eternal) {
812             this.timeToIdle = 0;
813             this.timeToLive = 0;
814         } else if (isEternal()) {
815             this.timeToIdle = Integer.MIN_VALUE;
816             this.timeToLive = Integer.MIN_VALUE;
817         } else {
818             timeToIdle = tti;
819             timeToLive = ttl;
820         }
821     }
822 
823     /***
824      * Custom serialization write logic
825      */
826     private void writeObject(ObjectOutputStream out) throws IOException {
827         if (!elementEvictionData.canParticipateInSerialization()) {
828             throw new NotSerializableException();
829         }
830         out.defaultWriteObject();
831         out.writeInt(TimeUtil.toSecs(elementEvictionData.getCreationTime()));
832         out.writeInt(TimeUtil.toSecs(elementEvictionData.getLastAccessTime()));
833     }
834 
835     /***
836      * Custom serialization read logic
837      */
838     private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException {
839         in.defaultReadObject();
840         elementEvictionData = new DefaultElementEvictionData(TimeUtil.toMillis(in.readInt()), TimeUtil.toMillis(in.readInt()));
841     }
842 }