This is an automated email from the ASF dual-hosted git repository.

tv pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/commons-jcs.git

commit f1c0edfbb6d0e5463365711e805bedab0c205e90
Author: Thomas Vandahl <[email protected]>
AuthorDate: Sun Feb 8 22:26:17 2026 +0100

    Make ElementAttributes (largely) immutable
---
 RECORDS_REFACTORING_EXAMPLE.md                     | 790 +++++++++++++++++++++
 .../apache/commons/jcs4/admin/JCSAdminBean.java    |   9 +-
 .../jcs4/auxiliary/disk/jdbc/JDBCDiskCache.java    |  12 +-
 .../commons/jcs4/engine/ElementAttributes.java     | 540 ++++++--------
 .../jcs4/engine/behavior/IElementAttributes.java   |  80 +--
 .../jcs4/engine/control/CompositeCache.java        |  28 +-
 .../engine/control/CompositeCacheConfigurator.java |  38 +-
 .../jcs4/engine/control/CompositeCacheManager.java |   2 +-
 .../engine/memory/shrinking/ShrinkerThread.java    |   4 +-
 .../serialization/SerializationConversionUtil.java |   6 +-
 commons-jcs4-core/src/test/conf/cache.ccf          |   2 +-
 commons-jcs4-core/src/test/conf/remote.cache.ccf   |   2 +-
 .../jcs4/JCSCacheElementRetrievalUnitTest.java     |   2 +-
 .../commons/jcs4/access/CacheAccessUnitTest.java   |  22 +-
 .../commons/jcs4/access/TestCacheAccess.java       |   2 +-
 .../disk/block/AbstractBlockDiskCacheUnitTest.java |  12 +-
 .../indexed/AbstractIndexDiskCacheUnitTest.java    |  16 +-
 .../indexed/IndexedDiskCacheKeyStoreUnitTest.java  |  12 +-
 .../remote/RemoteCacheListenerUnitTest.java        |  12 +-
 .../jcs4/engine/ElementAttributesUtils.java        |   5 +-
 .../control/event/SimpleEventHandlingUnitTest.java |  10 +-
 .../memory/shrinking/ShrinkerThreadUnitTest.java   |  58 +-
 .../SerializationConversionUtilUnitTest.java       |  20 +-
 .../src/test/test-conf/TestHSQLDiskCache.ccf       |   2 +-
 .../test/test-conf/TestHSQLDiskCacheConcurrent.ccf |   2 +-
 .../src/test/test-conf/TestJDBCDiskCache.ccf       |   2 +-
 .../test/test-conf/TestJDBCDiskCacheRemoval.ccf    |   2 +-
 .../test/test-conf/TestJDBCDiskCacheSharedPool.ccf |   2 +-
 .../src/test/test-conf/TestJDBCDiskCacheShrink.ccf |   2 +-
 .../src/test/test-conf/TestMRUCache.ccf            |   2 +-
 .../src/test/test-conf/TestMySQLDiskCache.ccf      |   2 +-
 .../src/test/test-conf/TestRemoteClient.ccf        |   2 +-
 .../src/test/test-conf/TestRemoteHttpCache.ccf     |   2 +-
 .../src/test/test-conf/TestRemoteServer.ccf        |   2 +-
 .../src/test/test-conf/TestSimpleEventHandling.ccf |   2 +-
 .../src/test/test-conf/TestSoftReferenceCache.ccf  |   2 +-
 .../src/test/test-conf/TestZeroSizeCache.ccf       |   2 +-
 .../org/apache/commons/jcs4/jcache/JCSCache.java   |  52 +-
 .../commons/jcs4/jcache/JCSCachingManager.java     |   2 +-
 xdocs/ElementAttributes.xml                        |   2 +-
 xdocs/IndexedDiskAuxCache.xml                      |   2 +-
 xdocs/JDBCDiskCache.xml                            |   6 +-
 xdocs/UpgradingFrom3x.xml                          |  11 +-
 xdocs/UsingJCSBasicWeb.xml                         |   4 +-
 xdocs/getting_started/intro.xml                    |   2 +-
 45 files changed, 1235 insertions(+), 558 deletions(-)

diff --git a/RECORDS_REFACTORING_EXAMPLE.md b/RECORDS_REFACTORING_EXAMPLE.md
new file mode 100644
index 00000000..e2b49ae2
--- /dev/null
+++ b/RECORDS_REFACTORING_EXAMPLE.md
@@ -0,0 +1,790 @@
+<!---
+ Licensed to the Apache Software Foundation (ASF) under one or more
+ contributor license agreements.  See the NOTICE file distributed with
+ this work for additional information regarding copyright ownership.
+ The ASF licenses this file to You under the Apache License, Version 2.0
+ (the "License"); you may not use this file except in compliance with
+ the License.  You may obtain a copy of the License at
+
+      https://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+# Refactoring PropertySetter Pattern to Java Records
+
+## Current Approach: PropertySetter with Reflection
+
+The current codebase uses `PropertySetter` (similar to log4j's implementation) 
to dynamically populate mutable objects from `Properties` using reflection and 
JavaBeans introspection.
+
+**Example at line 180 in CompositeCacheConfigurator:**
+```java
+AuxiliaryCacheAttributes auxAttr = ccm.registryAttrGet( auxName );
+auxAttr = auxAttr.clone();
+PropertySetter.setProperties( auxAttr, props, attrName + "." );
+auxAttr.setCacheName( regName );
+```
+
+The pattern:
+1. Create/clone a mutable object
+2. Use reflection to introspect setter methods
+3. Parse and convert property strings to target types
+4. Invoke setters via reflection
+5. Manual post-setup (e.g., `setCacheName()`)
+
+---
+
+## Problems with Current Approach
+
+- **Boilerplate**: Each attribute class needs getters/setters for every 
property
+- **No compile-time safety**: Property names and types are strings, discovered 
at runtime
+- **Mutability**: Objects are mutable, making debugging harder
+- **Performance**: Reflection overhead, introspection on every configuration 
load
+- **Inheritance complexity**: Child classes override parent setters, must 
maintain parallel hierarchies
+- **Cloning requirement**: Objects must be cloneable for reuse, adds complexity
+
+---
+
+## Solution: Java Records + Builder Pattern
+
+Java Records (Java 14+, finalized in Java 16) provide immutable, compact data 
carriers. Combined with a builder pattern, they eliminate reflection while 
maintaining flexibility.
+
+### Approach 1: Simple Record + Static Builder
+
+**Before:**
+```java
+// CompositeCacheAttributes.java (current)
+public class CompositeCacheAttributes implements ICompositeCacheAttributes {
+    private boolean useLateral = DEFAULT_USE_LATERAL;
+    private boolean useRemote = DEFAULT_USE_REMOTE;
+    private boolean useDisk = DEFAULT_USE_DISK;
+    private int maxObjs = DEFAULT_MAX_OBJECTS;
+    private long maxMemoryIdleTimeSeconds = 
DEFAULT_MAX_MEMORY_IDLE_TIME_SECONDS;
+    private String cacheName;
+    private String memoryCacheName;
+    // ... 15+ more properties
+    
+    public void setUseLateral(boolean val) { this.useLateral = val; }
+    public void setUseRemote(boolean val) { this.useRemote = val; }
+    public void setUseDisk(boolean val) { this.useDisk = val; }
+    // ... more setters
+}
+```
+
+**After with Records:**
+```java
+public record CompositeCacheAttributes(
+    boolean useLateral,
+    boolean useRemote,
+    boolean useDisk,
+    boolean useMemoryShrinker,
+    int maxObjs,
+    long maxMemoryIdleTimeSeconds,
+    long shrinkerIntervalSeconds,
+    int maxSpoolPerRun,
+    String cacheName,
+    String memoryCacheName,
+    DiskUsagePattern diskUsagePattern
+) implements ICompositeCacheAttributes {
+    
+    // Compact constructor for validation
+    public CompositeCacheAttributes {
+        // Add validation here if needed
+    }
+    
+    // Static factory with builder
+    public static Builder builder() {
+        return new Builder();
+    }
+    
+    public static class Builder {
+        private boolean useLateral = DEFAULT_USE_LATERAL;
+        private boolean useRemote = DEFAULT_USE_REMOTE;
+        private boolean useDisk = DEFAULT_USE_DISK;
+        private boolean useMemoryShrinker = DEFAULT_USE_SHRINKER;
+        private int maxObjs = DEFAULT_MAX_OBJECTS;
+        private long maxMemoryIdleTimeSeconds = 
DEFAULT_MAX_MEMORY_IDLE_TIME_SECONDS;
+        private long shrinkerIntervalSeconds = 
DEFAULT_SHRINKER_INTERVAL_SECONDS;
+        private int maxSpoolPerRun = DEFAULT_MAX_SPOOL_PER_RUN;
+        private String cacheName;
+        private String memoryCacheName;
+        private DiskUsagePattern diskUsagePattern = DiskUsagePattern.SWAP;
+        
+        public Builder useLateral(boolean val) { this.useLateral = val; return 
this; }
+        public Builder useRemote(boolean val) { this.useRemote = val; return 
this; }
+        public Builder useDisk(boolean val) { this.useDisk = val; return this; 
}
+        public Builder useMemoryShrinker(boolean val) { this.useMemoryShrinker 
= val; return this; }
+        public Builder maxObjs(int val) { this.maxObjs = val; return this; }
+        public Builder maxMemoryIdleTimeSeconds(long val) { 
this.maxMemoryIdleTimeSeconds = val; return this; }
+        public Builder shrinkerIntervalSeconds(long val) { 
this.shrinkerIntervalSeconds = val; return this; }
+        public Builder maxSpoolPerRun(int val) { this.maxSpoolPerRun = val; 
return this; }
+        public Builder cacheName(String val) { this.cacheName = val; return 
this; }
+        public Builder memoryCacheName(String val) { this.memoryCacheName = 
val; return this; }
+        public Builder diskUsagePattern(DiskUsagePattern val) { 
this.diskUsagePattern = val; return this; }
+        
+        public CompositeCacheAttributes build() {
+            return new CompositeCacheAttributes(
+                useLateral, useRemote, useDisk, useMemoryShrinker,
+                maxObjs, maxMemoryIdleTimeSeconds, shrinkerIntervalSeconds,
+                maxSpoolPerRun, cacheName, memoryCacheName, diskUsagePattern
+            );
+        }
+    }
+}
+```
+
+### Usage Comparison
+
+**Current (PropertySetter with reflection):**
+```java
+ICompositeCacheAttributes ccAttr = new CompositeCacheAttributes();
+PropertySetter.setProperties(ccAttr, props, attrName + ".");
+ccAttr.setCacheName(regName);
+```
+
+**New (Type-safe builder):**
+```java
+var builder = ICompositeCacheAttributes.builder();
+for (String key : props.stringPropertyNames()) {
+    if (key.startsWith(attrName + ".")) {
+        String propName = key.substring(attrName.length() + 1);
+        String value = props.getProperty(key);
+        builder.setPropertyByName(propName, value);  // see below
+    }
+}
+ICompositeCacheAttributes ccAttr = builder.cacheName(regName).build();
+```
+
+---
+
+### Approach 2: Type-Safe Configuration Parser (Recommended)
+
+Instead of generic `PropertySetter`, create a **configuration parser** 
specific to each record type:
+
+```java
+public record CompositeCacheAttributes(...) implements 
ICompositeCacheAttributes {
+    
+    public static class Parser {
+        private static final Map<String, BiConsumer<Builder, String>> 
PROPERTY_SETTERS = Map.ofEntries(
+            Map.entry("useLateral", (b, v) -> 
b.useLateral(Boolean.parseBoolean(v))),
+            Map.entry("useRemote", (b, v) -> 
b.useRemote(Boolean.parseBoolean(v))),
+            Map.entry("useDisk", (b, v) -> b.useDisk(Boolean.parseBoolean(v))),
+            Map.entry("useMemoryShrinker", (b, v) -> 
b.useMemoryShrinker(Boolean.parseBoolean(v))),
+            Map.entry("maxObjs", (b, v) -> b.maxObjs(Integer.parseInt(v))),
+            Map.entry("maxMemoryIdleTimeSeconds", (b, v) -> 
b.maxMemoryIdleTimeSeconds(Long.parseLong(v))),
+            Map.entry("shrinkerIntervalSeconds", (b, v) -> 
b.shrinkerIntervalSeconds(Long.parseLong(v))),
+            Map.entry("maxSpoolPerRun", (b, v) -> 
b.maxSpoolPerRun(Integer.parseInt(v))),
+            Map.entry("memoryCacheName", (b, v) -> b.memoryCacheName(v)),
+            Map.entry("diskUsagePattern", (b, v) -> 
b.diskUsagePattern(DiskUsagePattern.valueOf(v.toUpperCase())))
+        );
+        
+        public static CompositeCacheAttributes fromProperties(
+                Properties props, 
+                String prefix, 
+                String cacheName) {
+            var builder = CompositeCacheAttributes.builder();
+            builder.cacheName(cacheName);
+            
+            final int prefixLen = prefix.length();
+            for (String key : props.stringPropertyNames()) {
+                if (key.startsWith(prefix)) {
+                    // Ignore nested properties (containing dots after prefix)
+                    if (key.indexOf('.', prefixLen + 1) > 0) {
+                        continue;
+                    }
+                    
+                    String propName = key.substring(prefixLen);
+                    String value = props.getProperty(key);
+                    
+                    var setter = PROPERTY_SETTERS.get(propName);
+                    if (setter != null) {
+                        try {
+                            setter.accept(builder, value);
+                        } catch (NumberFormatException | 
IllegalArgumentException e) {
+                            log.warn("Failed to parse property {}: {}", key, 
e.getMessage());
+                        }
+                    } else {
+                        log.warn("Unknown property: {}", propName);
+                    }
+                }
+            }
+            
+            return builder.build();
+        }
+    }
+}
+```
+
+**Usage:**
+```java
+ICompositeCacheAttributes ccAttr = 
CompositeCacheAttributes.Parser.fromProperties(
+    props, attrName + ".", regName
+);
+```
+
+**Benefits:**
+- ✅ Type-safe: Each property has explicit type conversion
+- ✅ Compile-time safety: Property names are constants in the map
+- ✅ No reflection: Direct method calls via `BiConsumer`
+- ✅ Better error handling: Specific exception handling per property type
+- ✅ Immutable: Records are immutable by default
+- ✅ Performance: No introspection overhead
+- ✅ IDE support: Auto-completion and refactoring work perfectly
+
+---
+
+### Approach 3: Annotation-Based Configuration (Most Elegant)
+
+For a more scalable solution that reduces boilerplate, use annotations:
+
+#### Annotation Definition
+
+```java
+package org.apache.commons.jcs4.utils.config;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * Marks a record component as a configurable property that can be loaded from 
Properties.
+ * Applied to individual record components.
+ */
+@Retention(RetentionPolicy.RUNTIME)
+@Target(ElementType.RECORD_COMPONENT)
+public @interface ConfigurableProperty {
+    
+    /**
+     * The property name in the configuration file.
+     * If not specified, defaults to the record component name.
+     */
+    String name() default "";
+    
+    /**
+     * The type of conversion needed: "boolean", "int", "long", "double", 
"string", "enum"
+     */
+    String type();
+    
+    /**
+     * Default value if property is not provided (as a string).
+     */
+    String defaultValue() default "";
+    
+    /**
+     * Whether this property is required (no default).
+     */
+    boolean required() default false;
+    
+    /**
+     * For enum types, the enum class name (if type="enum").
+     */
+    String enumClass() default "";
+}
+
+/**
+ * Applied to record classes to enable configuration parsing.
+ */
+@Retention(RetentionPolicy.RUNTIME)
+@Target(ElementType.TYPE)
+public @interface Configurable {
+    /**
+     * Optional description of what this configuration represents.
+     */
+    String description() default "";
+}
+```
+
+#### Record Definition
+
+```java
+@Configurable(description = "Composite cache region attributes")
+public record CompositeCacheAttributes(
+    @ConfigurableProperty(name = "useLateral", type = "boolean", defaultValue 
= "true")
+    boolean useLateral,
+    
+    @ConfigurableProperty(name = "useRemote", type = "boolean", defaultValue = 
"true")
+    boolean useRemote,
+    
+    @ConfigurableProperty(name = "useDisk", type = "boolean", defaultValue = 
"true")
+    boolean useDisk,
+    
+    @ConfigurableProperty(name = "useMemoryShrinker", type = "boolean", 
defaultValue = "false")
+    boolean useMemoryShrinker,
+    
+    @ConfigurableProperty(name = "maxObjs", type = "int", defaultValue = "100")
+    int maxObjs,
+    
+    @ConfigurableProperty(name = "maxMemoryIdleTimeSeconds", type = "long", 
defaultValue = "7200")
+    long maxMemoryIdleTimeSeconds,
+    
+    @ConfigurableProperty(name = "shrinkerIntervalSeconds", type = "long", 
defaultValue = "30")
+    long shrinkerIntervalSeconds,
+    
+    @ConfigurableProperty(name = "maxSpoolPerRun", type = "int", defaultValue 
= "-1")
+    int maxSpoolPerRun,
+    
+    @ConfigurableProperty(name = "memoryCacheName", type = "string", 
+                         defaultValue = 
"org.apache.commons.jcs4.engine.memory.lru.LRUMemoryCache")
+    String memoryCacheName,
+    
+    @ConfigurableProperty(name = "diskUsagePattern", type = "enum", 
+                         enumClass = 
"org.apache.commons.jcs4.engine.DiskUsagePattern", 
+                         defaultValue = "SWAP")
+    DiskUsagePattern diskUsagePattern,
+    
+    // Not annotated - injected programmatically
+    String cacheName
+) implements ICompositeCacheAttributes {
+    
+    public static CompositeCacheAttributes fromProperties(
+            Properties props, 
+            String prefix, 
+            String cacheName) {
+        return ConfigurationBuilder.create(CompositeCacheAttributes.class)
+            .fromProperties(props, prefix)
+            .set("cacheName", cacheName)
+            .build();
+    }
+}
+```
+
+#### Generic Configuration Builder
+
+```java
+package org.apache.commons.jcs4.utils.config;
+
+import java.lang.reflect.Constructor;
+import java.lang.reflect.RecordComponent;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Properties;
+
+/**
+ * Generic builder for record types annotated with @Configurable.
+ * Automatically parses Properties and creates record instances.
+ */
+public class ConfigurationBuilder<T> {
+    
+    private static final Log log = Log.getLog(ConfigurationBuilder.class);
+    
+    private final Class<T> recordClass;
+    private final Map<String, Object> values = new HashMap<>();
+    private final Map<String, String> errors = new HashMap<>();
+    
+    private ConfigurationBuilder(Class<T> recordClass) {
+        if (!recordClass.isRecord()) {
+            throw new IllegalArgumentException(recordClass.getName() + " is 
not a record");
+        }
+        this.recordClass = recordClass;
+        initializeDefaults();
+    }
+    
+    /**
+     * Create a new builder for the given record class.
+     */
+    public static <T> ConfigurationBuilder<T> create(Class<T> recordClass) {
+        return new ConfigurationBuilder<>(recordClass);
+    }
+    
+    /**
+     * Initialize default values from annotations.
+     */
+    private void initializeDefaults() {
+        RecordComponent[] components = recordClass.getRecordComponents();
+        for (RecordComponent component : components) {
+            ConfigurableProperty prop = 
component.getAnnotation(ConfigurableProperty.class);
+            if (prop != null && !prop.defaultValue().isEmpty()) {
+                Object value = parseValue(prop.defaultValue(), prop.type(), 
prop.enumClass());
+                if (value != null) {
+                    values.put(component.getName(), value);
+                }
+            }
+        }
+    }
+    
+    /**
+     * Load properties from a Properties object with a given prefix.
+     */
+    public ConfigurationBuilder<T> fromProperties(Properties props, String 
prefix) {
+        if (props == null || props.isEmpty()) {
+            return this;
+        }
+        
+        RecordComponent[] components = recordClass.getRecordComponents();
+        final int prefixLen = prefix.length();
+        
+        for (RecordComponent component : components) {
+            ConfigurableProperty prop = 
component.getAnnotation(ConfigurableProperty.class);
+            if (prop == null) {
+                continue;
+            }
+            
+            String propName = prop.name().isEmpty() ? component.getName() : 
prop.name();
+            String fullKey = prefix + propName;
+            String value = props.getProperty(fullKey);
+            
+            if (value != null) {
+                try {
+                    Object parsed = parseValue(value, prop.type(), 
prop.enumClass());
+                    if (parsed != null) {
+                        values.put(component.getName(), parsed);
+                        log.debug("Loaded property {}: {}", fullKey, parsed);
+                    }
+                } catch (Exception e) {
+                    String errMsg = String.format(
+                        "Failed to parse property '%s' with value '%s' as %s", 
+                        fullKey, value, prop.type()
+                    );
+                    errors.put(component.getName(), errMsg);
+                    log.warn("{}: {}", errMsg, e.getMessage());
+                }
+            } else if (prop.required()) {
+                String errMsg = String.format("Required property '%s' not 
found", fullKey);
+                errors.put(component.getName(), errMsg);
+                log.error(errMsg);
+            }
+        }
+        
+        return this;
+    }
+    
+    /**
+     * Explicitly set a property value.
+     */
+    public ConfigurationBuilder<T> set(String componentName, Object value) {
+        if (value != null) {
+            values.put(componentName, value);
+        }
+        return this;
+    }
+    
+    /**
+     * Explicitly set a property value from a string.
+     */
+    public ConfigurationBuilder<T> setProperty(String componentName, String 
value) {
+        RecordComponent component = findComponent(componentName);
+        if (component == null) {
+            log.warn("No record component named: {}", componentName);
+            return this;
+        }
+        
+        ConfigurableProperty prop = 
component.getAnnotation(ConfigurableProperty.class);
+        if (prop != null) {
+            try {
+                Object parsed = parseValue(value, prop.type(), 
prop.enumClass());
+                if (parsed != null) {
+                    values.put(componentName, parsed);
+                }
+            } catch (Exception e) {
+                errors.put(componentName, e.getMessage());
+                log.warn("Failed to set property {}: {}", componentName, 
e.getMessage());
+            }
+        } else {
+            values.put(componentName, value);
+        }
+        
+        return this;
+    }
+    
+    /**
+     * Check if there were any errors during configuration.
+     */
+    public boolean hasErrors() {
+        return !errors.isEmpty();
+    }
+    
+    /**
+     * Get all configuration errors.
+     */
+    public Map<String, String> getErrors() {
+        return new HashMap<>(errors);
+    }
+    
+    /**
+     * Build the record instance, throwing if any required properties are 
missing.
+     */
+    public T build() {
+        // Validate all required properties are present
+        for (RecordComponent component : recordClass.getRecordComponents()) {
+            ConfigurableProperty prop = 
component.getAnnotation(ConfigurableProperty.class);
+            if (prop != null && prop.required() && 
!values.containsKey(component.getName())) {
+                throw new IllegalStateException(
+                    "Required property missing: " + component.getName()
+                );
+            }
+        }
+        
+        return createInstance();
+    }
+    
+    /**
+     * Build the record instance, returning null if any errors occurred.
+     */
+    public T buildSafely() {
+        if (hasErrors()) {
+            return null;
+        }
+        return createInstance();
+    }
+    
+    /**
+     * Create the record instance using the canonical constructor.
+     */
+    private T createInstance() {
+        try {
+            RecordComponent[] components = recordClass.getRecordComponents();
+            Class<?>[] paramTypes = new Class<?>[components.length];
+            Object[] params = new Object[components.length];
+            
+            for (int i = 0; i < components.length; i++) {
+                paramTypes[i] = components[i].getType();
+                Object value = values.get(components[i].getName());
+                if (value == null) {
+                    // Use null-safe defaults for primitives
+                    value = getDefaultForType(components[i].getType());
+                }
+                params[i] = value;
+            }
+            
+            Constructor<T> constructor = 
recordClass.getDeclaredConstructor(paramTypes);
+            T instance = constructor.newInstance(params);
+            log.debug("Created instance of {}", recordClass.getSimpleName());
+            return instance;
+            
+        } catch (Exception e) {
+            throw new RuntimeException(
+                "Failed to create instance of " + recordClass.getName(), e
+            );
+        }
+    }
+    
+    /**
+     * Parse a string value to the appropriate type.
+     */
+    private Object parseValue(String value, String type, String enumClassName) 
{
+        if (value == null || value.trim().isEmpty()) {
+            return null;
+        }
+        
+        String trimmed = value.trim();
+        
+        try {
+            return switch (type.toLowerCase()) {
+                case "boolean" -> Boolean.parseBoolean(trimmed);
+                case "int" -> Integer.parseInt(trimmed);
+                case "long" -> Long.parseLong(trimmed);
+                case "double" -> Double.parseDouble(trimmed);
+                case "string" -> value;
+                case "enum" -> parseEnum(trimmed, enumClassName);
+                default -> {
+                    log.warn("Unknown type for conversion: {}", type);
+                    yield value;
+                }
+            };
+        } catch (Exception e) {
+            throw new RuntimeException(
+                String.format("Cannot convert '%s' to type %s", value, type), e
+            );
+        }
+    }
+    
+    /**
+     * Parse an enum value.
+     */
+    @SuppressWarnings("unchecked")
+    private Object parseEnum(String value, String enumClassName) throws 
Exception {
+        Class<? extends Enum> enumClass = (Class<? extends Enum>) 
Class.forName(enumClassName);
+        return Enum.valueOf(enumClass, value.toUpperCase());
+    }
+    
+    /**
+     * Get default value for a primitive type.
+     */
+    private Object getDefaultForType(Class<?> type) {
+        if (type == boolean.class) return false;
+        if (type == int.class) return 0;
+        if (type == long.class) return 0L;
+        if (type == double.class) return 0.0d;
+        if (type == float.class) return 0.0f;
+        return null;
+    }
+    
+    /**
+     * Find a record component by name.
+     */
+    private RecordComponent findComponent(String name) {
+        for (RecordComponent component : recordClass.getRecordComponents()) {
+            if (component.getName().equals(name)) {
+                return component;
+            }
+        }
+        return null;
+    }
+}
+```
+
+#### Usage Examples
+
+```java
+// Simple case: load from properties
+Properties props = new Properties();
+props.load(new FileInputStream("cache.properties"));
+CompositeCacheAttributes attrs = ConfigurationBuilder
+    .create(CompositeCacheAttributes.class)
+    .fromProperties(props, "jcs.region.myregion.cacheattributes.")
+    .set("cacheName", "myregion")
+    .build();
+
+// With error handling
+var builder = ConfigurationBuilder.create(CompositeCacheAttributes.class)
+    .fromProperties(props, "jcs.region.myregion.cacheattributes.")
+    .set("cacheName", "myregion");
+
+if (builder.hasErrors()) {
+    builder.getErrors().forEach((prop, error) ->
+        log.error("Configuration error in {}: {}", prop, error)
+    );
+    return null;
+}
+
+CompositeCacheAttributes attrs = builder.build();
+
+// In CompositeCacheConfigurator.parseCompositeCacheAttributes()
+protected ICompositeCacheAttributes parseCompositeCacheAttributes(
+        final Properties props,
+        final String regName,
+        final ICompositeCacheAttributes defaultCCAttr,
+        final String regionPrefix) {
+    
+    final String attrName = regionPrefix + regName + CACHE_ATTRIBUTE_PREFIX;
+    
+    try {
+        var builder = 
ConfigurationBuilder.create(CompositeCacheAttributes.class)
+            .fromProperties(props, attrName + ".");
+        
+        if (builder.hasErrors()) {
+            log.error("Configuration errors for region {}: {}", 
+                regName, builder.getErrors());
+            return defaultCCAttr;
+        }
+        
+        return builder.set("cacheName", regName).build();
+    } catch (Exception e) {
+        log.error("Failed to parse cache attributes for region {}", regName, 
e);
+        return defaultCCAttr;
+    }
+}
+
+// Works with other record types too (ElementAttributes, etc.)
+ElementAttributes elemAttrs = ConfigurationBuilder
+    .create(ElementAttributes.class)
+    .fromProperties(props, "jcs.region.myregion.elementattributes.")
+    .build();
+```
+
+#### Advantages of Annotation-Based Approach
+
+- **Single Source of Truth**: Metadata is embedded in the record definition
+- **Type-Safe**: Compiler ensures properties exist and have correct types
+- **Minimal Boilerplate**: No need to write parser classes for each record type
+- **Reusable Infrastructure**: `ConfigurationBuilder` works for any 
`@Configurable` record
+- **Runtime Flexibility**: Properties can be added without recompilation
+- **Better IDE Support**: Annotations provide navigation and quick-fixes
+- **Self-Documenting**: Annotations serve as documentation
+- **Extensible**: Easy to add new annotation features (validation, 
transformation, etc.)
+```
+
+---
+
+## Handling Inheritance (Child Types)
+
+If you have child attribute classes inheriting from parent classes, records 
handle this differently:
+
+**Current (with inheritance):**
+```java
+public abstract class AbstractAuxiliaryCacheAttributes { ... }
+public class JDBCAuxiliaryCacheAttributes extends 
AbstractAuxiliaryCacheAttributes { ... }
+```
+
+**With Records (sealed types):**
+```java
+// Base record
+public record AuxiliaryCacheAttributes(
+    String name,
+    int maxFailureWaitTimeSeconds,
+    long failureCountWaitTimeSeconds,
+    // common properties
+) { }
+
+// Sealed type hierarchy (Java 17+)
+public sealed record JDBCAuxiliaryCacheAttributes(
+    String name,
+    int maxFailureWaitTimeSeconds,
+    long failureCountWaitTimeSeconds,
+    String username,
+    String password,
+    String driver,
+    // JDBC-specific properties
+) extends AuxiliaryCacheAttributes { }
+
+public sealed record RemoteAuxiliaryCacheAttributes(
+    String name,
+    int maxFailureWaitTimeSeconds,
+    long failureCountWaitTimeSeconds,
+    String remoteHost,
+    int remotePort,
+    // Remote-specific properties
+) extends AuxiliaryCacheAttributes { }
+```
+
+---
+
+## Migration Strategy
+
+1. **Phase 1**: Create new record classes alongside existing ones
+2. **Phase 2**: Add parsers for records (Approach 2 above)
+3. **Phase 3**: Update configurators to use new parsers
+4. **Phase 4**: Gradually deprecate old attribute classes
+5. **Phase 5**: Remove PropertySetter dependency
+
+**Example migration in CompositeCacheConfigurator:**
+
+```java
+// OLD (lines 258-266)
+protected ICompositeCacheAttributes parseCompositeCacheAttributes(...) {
+    ICompositeCacheAttributes ccAttr = new CompositeCacheAttributes();
+    final String attrName = regionPrefix + regName + CACHE_ATTRIBUTE_PREFIX;
+    PropertySetter.setProperties(ccAttr, props, attrName + ".");
+    ccAttr.setCacheName(regName);
+    return ccAttr;
+}
+
+// NEW
+protected ICompositeCacheAttributes parseCompositeCacheAttributes(...) {
+    final String attrName = regionPrefix + regName + CACHE_ATTRIBUTE_PREFIX;
+    return CompositeCacheAttributes.Parser.fromProperties(
+        props, attrName + ".", regName
+    );
+}
+```
+
+---
+
+## Summary
+
+| Aspect | PropertySetter | Record + Builder | Record + Parser | Record + 
Annotation |
+|--------|---|---|---|---|
+| Boilerplate | High | Medium | Low | Very Low |
+| Type Safety | Low | High | High | High |
+| Performance | Low (Reflection) | High | High | Medium |
+| Error Handling | Generic | Better | Best | Good |
+| IDE Support | Limited | Excellent | Excellent | Excellent |
+| Maintenance | Difficult | Easier | Easy | Easiest |
+| Learning Curve | Moderate | Low | Low | Medium |
+| Immutability | No | Yes | Yes | Yes |
+
+**Recommendation**: Start with **Approach 2 (Type-Safe Parser)** for immediate 
benefits with minimal refactoring. Evolve to **Approach 3 (Annotation-Based)** 
if configuration complexity grows.
diff --git 
a/commons-jcs4-core/src/main/java/org/apache/commons/jcs4/admin/JCSAdminBean.java
 
b/commons-jcs4-core/src/main/java/org/apache/commons/jcs4/admin/JCSAdminBean.java
index ef597cfb..17478d19 100644
--- 
a/commons-jcs4-core/src/main/java/org/apache/commons/jcs4/admin/JCSAdminBean.java
+++ 
b/commons-jcs4-core/src/main/java/org/apache/commons/jcs4/admin/JCSAdminBean.java
@@ -140,15 +140,14 @@ public class JCSAdminBean implements JCSJMXBean
         for (final Map.Entry<String, ?> key : keys.entrySet())
         {
             final ICacheElement<?, ?> element = 
cache.getMemoryCache().getQuiet( key.getValue() );
-
             final IElementAttributes attributes = 
element.getElementAttributes();
 
             final CacheElementInfo elementInfo = new CacheElementInfo(
                        key.getKey(),
-                       attributes.getIsEternal(),
-                       format.format(new Date(attributes.getCreateTime())),
-                       attributes.getMaxLife(),
-                       (now - attributes.getCreateTime() - 
attributes.getMaxLife() * 1000 ) / -1000);
+                       attributes.isEternal(),
+                       format.format(new Date(attributes.createTime())),
+                       attributes.maxLife(),
+                       (now - attributes.createTime() - attributes.maxLife() * 
1000 ) / -1000);
 
             records.add( elementInfo );
         }
diff --git 
a/commons-jcs4-core/src/main/java/org/apache/commons/jcs4/auxiliary/disk/jdbc/JDBCDiskCache.java
 
b/commons-jcs4-core/src/main/java/org/apache/commons/jcs4/auxiliary/disk/jdbc/JDBCDiskCache.java
index ee426604..38b57971 100644
--- 
a/commons-jcs4-core/src/main/java/org/apache/commons/jcs4/auxiliary/disk/jdbc/JDBCDiskCache.java
+++ 
b/commons-jcs4-core/src/main/java/org/apache/commons/jcs4/auxiliary/disk/jdbc/JDBCDiskCache.java
@@ -458,16 +458,16 @@ public class JDBCDiskCache<K, V>
             psInsert.setString( 1, ce.getKey().toString() );
             psInsert.setString( 2, getCacheName() );
             psInsert.setBytes( 3, element );
-            psInsert.setLong( 4, ce.getElementAttributes().getMaxLife() );
-            psInsert.setString( 5, ce.getElementAttributes().getIsEternal() ? 
"T" : "F" );
+            psInsert.setLong( 4, ce.getElementAttributes().maxLife() );
+            psInsert.setString( 5, ce.getElementAttributes().isEternal() ? "T" 
: "F" );
 
-            final Timestamp createTime = new Timestamp( 
ce.getElementAttributes().getCreateTime() );
+            final Timestamp createTime = new Timestamp( 
ce.getElementAttributes().createTime() );
             psInsert.setTimestamp( 6, createTime );
 
             final long now = System.currentTimeMillis() / 1000;
             psInsert.setLong( 7, now );
 
-            final long expireTime = now + 
ce.getElementAttributes().getMaxLife();
+            final long expireTime = now + ce.getElementAttributes().maxLife();
             psInsert.setLong( 8, expireTime );
 
             psInsert.execute();
@@ -508,13 +508,13 @@ public class JDBCDiskCache<K, V>
         {
             psUpdate.setBytes( 1, element );
 
-            final Timestamp createTime = new Timestamp( 
ce.getElementAttributes().getCreateTime() );
+            final Timestamp createTime = new Timestamp( 
ce.getElementAttributes().createTime() );
             psUpdate.setTimestamp( 2, createTime );
 
             final long now = System.currentTimeMillis() / 1000;
             psUpdate.setLong( 3, now );
 
-            final long expireTime = now + 
ce.getElementAttributes().getMaxLife();
+            final long expireTime = now + ce.getElementAttributes().maxLife();
             psUpdate.setLong( 4, expireTime );
 
             psUpdate.setString( 5, (String) ce.getKey() );
diff --git 
a/commons-jcs4-core/src/main/java/org/apache/commons/jcs4/engine/ElementAttributes.java
 
b/commons-jcs4-core/src/main/java/org/apache/commons/jcs4/engine/ElementAttributes.java
index 7cf67ab7..01a05c5f 100644
--- 
a/commons-jcs4-core/src/main/java/org/apache/commons/jcs4/engine/ElementAttributes.java
+++ 
b/commons-jcs4-core/src/main/java/org/apache/commons/jcs4/engine/ElementAttributes.java
@@ -21,245 +21,298 @@ package org.apache.commons.jcs4.engine;
 
 import java.util.ArrayList;
 import java.util.List;
+import java.util.concurrent.atomic.AtomicLong;
 
 import org.apache.commons.jcs4.engine.behavior.IElementAttributes;
 import 
org.apache.commons.jcs4.engine.control.event.behavior.IElementEventHandler;
 
 /**
- * This it the element attribute descriptor class. Each element in the cache 
has an ElementAttribute
+ * This it the element attribute descriptor class. Each element in the cache 
has an ElementAttributes
  * object associated with it. An ElementAttributes object can be associated 
with an element in 3
  * ways:
  * <ol>
  * <li>When the item is put into the cache, you can associate an element 
attributes object.</li>
- * <li>If not attributes object is include when the element is put into the 
cache, then the default
+ * <li>If no attributes object is specified when the element is put into the 
cache, then the default
  * attributes for the region will be used.</li>
  * <li>The element attributes can be reset. This effectively results in a 
retrieval followed by a
  * put. Hence, this is the same as 1.</li>
  * </ol>
  */
-public class ElementAttributes
-    implements IElementAttributes
+public record ElementAttributes(
+        /** Can this item be flushed to disk */
+        boolean isSpool,
+
+        /** Is this item laterally distributable */
+        boolean isLateral,
+
+        /** Can this item be sent to the remote cache */
+        boolean isRemote,
+
+        /**
+         * You can turn off expiration by setting this to true. This causes 
the cache to bypass both max
+         * life and idle time expiration.
+         */
+        boolean isEternal,
+
+        /** Max life seconds */
+        long maxLife,
+
+        /**
+         * The maximum time an entry can be idle. Setting this to -1 causes 
the idle time check to be
+         * ignored.
+         */
+        long maxIdleTime,
+
+        /** The byte size of the field. Must be manually set. */
+        int size,
+
+        /** The creation time. This is used to enforce the max life. */
+        long createTime,
+
+        /** The last access time. This is used to enforce the max idle time. */
+        AtomicLong atomicLastAccessTime,
+
+        /** The time factor to convert durations to milliseconds */
+        long timeFactorForMilliseconds,
+
+        /**
+         * The list of Event handlers to use. This is transient, since the 
event handlers cannot usually
+         * be serialized. This means that you cannot attach a post 
serialization event to an item.
+         * <p>
+         * TODO we need to check that when an item is passed to a non-local 
cache that if the local
+         * cache had a copy with event handlers, that those handlers are used.
+         */
+        ArrayList<IElementEventHandler> elementEventHandlers
+) implements IElementAttributes
 {
     /** Don't change. */
     private static final long serialVersionUID = 7814990748035017441L;
 
-    /** Can this item be flushed to disk */
-    private boolean IS_SPOOL = true;
-
-    /** Is this item laterally distributable */
-    private boolean IS_LATERAL = true;
-
-    /** Can this item be sent to the remote cache */
-    private boolean IS_REMOTE = true;
-
-    /**
-     * You can turn off expiration by setting this to true. This causes the 
cache to bypass both max
-     * life and idle time expiration.
-     */
-    private boolean IS_ETERNAL = true;
-
-    /** Max life seconds */
-    private long maxLife = -1;
-
-    /**
-     * The maximum time an entry can be idle. Setting this to -1 causes the 
idle time check to be
-     * ignored.
-     */
-    private long maxIdleTime = -1;
-
-    /** The byte size of the field. Must be manually set. */
-    private int size;
-
-    /** The creation time. This is used to enforce the max life. */
-    private long createTime;
-
-    /** The last access time. This is used to enforce the max idel time. */
-    private long lastAccessTime;
+    /** Default */
+    private static final boolean DEFAULT_IS_SPOOL = true;
+    /** Default */
+    private static final boolean DEFAULT_IS_LATERAL = true;
+    /** Default */
+    private static final boolean DEFAULT_IS_REMOTE = true;
+    /** Default */
+    private static final boolean DEFAULT_IS_ETERNAL = true;
+    /** Default */
+    private static final long DEFAULT_MAX_LIFE = -1;
+    /** Default */
+    private static final long DEFAULT_MAX_IDLE_TIME = -1;
+    /** Default */
+    private static final long DEFAULT_TIME_FACTOR = 1000;
+
+    /** Record with all defaults set */
+    private static final ElementAttributes DEFAULT = new ElementAttributes(
+            DEFAULT_IS_SPOOL,
+            DEFAULT_IS_LATERAL,
+            DEFAULT_IS_REMOTE,
+            DEFAULT_IS_ETERNAL,
+            DEFAULT_MAX_LIFE,
+            DEFAULT_MAX_IDLE_TIME,
+            0,
+            0,
+            new AtomicLong(),
+            DEFAULT_TIME_FACTOR,
+            new ArrayList<>());
 
     /**
-     * The list of Event handlers to use. This is transient, since the event 
handlers cannot usually
-     * be serialized. This means that you cannot attach a post serialization 
event to an item.
-     * <p>
-     * TODO we need to check that when an item is passed to a non-local cache 
that if the local
-     * cache had a copy with event handlers, that those handlers are used.
-     */
-    private transient ArrayList<IElementEventHandler> eventHandlers;
-
-    private long timeFactor = 1000;
-
-    /**
-     * Constructor for the IElementAttributes object
+     * @return an object containing the default settings
      */
-    public ElementAttributes()
+    public static ElementAttributes defaults()
     {
-        this.createTime = System.currentTimeMillis();
-        this.lastAccessTime = this.createTime;
+        return DEFAULT;
     }
 
     /**
-     * Constructor for the IElementAttributes object
-     *
-     * @param attr
+     * Constructor for the ElementAttributes object
      */
-    protected ElementAttributes( final ElementAttributes attr )
+    public ElementAttributes()
     {
-        IS_ETERNAL = attr.IS_ETERNAL;
-
-        // waterfall onto disk, for pure disk set memory to 0
-        IS_SPOOL = attr.IS_SPOOL;
-
-        // lateral
-        IS_LATERAL = attr.IS_LATERAL;
-
-        // central rmi store
-        IS_REMOTE = attr.IS_REMOTE;
-
-        maxLife = attr.maxLife;
-        // time-to-live
-        maxIdleTime = attr.maxIdleTime;
-        size = attr.size;
+        this(defaults());
+        this.atomicLastAccessTime.set(createTime());
     }
 
     /**
-     * Adds a ElementEventHandler. Handler's can be registered for multiple 
events. A registered
-     * handler will be called at every recognized event.
-     * <p>
-     * The alternative would be to register handlers for each event. Or maybe 
The handler interface
-     * should have a method to return whether it cares about certain events.
-     *
-     * @param eventHandler The ElementEventHandler to be added to the list.
+     * Copy constructor for the ElementAttributes object
      */
-    @Override
-    public void addElementEventHandler( final IElementEventHandler 
eventHandler )
+    public ElementAttributes(IElementAttributes from)
     {
-        // lazy here, no concurrency problems expected
-        if ( this.eventHandlers == null )
-        {
-            this.eventHandlers = new ArrayList<>();
-        }
-        this.eventHandlers.add( eventHandler );
+        this(from.isSpool(),
+             from.isLateral(),
+             from.isRemote(),
+             from.isEternal(),
+             from.maxLife(),
+             from.maxIdleTime(),
+             from.size(),
+             System.currentTimeMillis(),
+             new AtomicLong(from.lastAccessTime()),
+             from.timeFactorForMilliseconds(),
+             new ArrayList<>(from.elementEventHandlers()));
     }
 
     /**
-     * Sets the eventHandlers of the IElementAttributes object.
-     * <p>
-     * This add the references to the local list. Subsequent changes in the 
caller's list will not
-     * be reflected.
-     *
-     * @param eventHandlers List of IElementEventHandler objects
+     * Constructor for the ElementAttributes object
      */
-    @Override
-    public void addElementEventHandlers( final List<IElementEventHandler> 
eventHandlers )
+    public ElementAttributes(
+            boolean isSpool,
+            boolean isLateral,
+            boolean isRemote,
+            boolean isEternal,
+            long maxLife,
+            long maxIdleTime,
+            long timeFactorForMilliseconds
+          )
     {
-        if ( eventHandlers == null )
-        {
-            return;
-        }
+        this(isSpool, isLateral, isRemote, isEternal, maxLife, maxIdleTime, 0,
+                System.currentTimeMillis(), new AtomicLong(), 
timeFactorForMilliseconds,
+                new ArrayList<>());
 
-        for (final IElementEventHandler handler : eventHandlers)
-        {
-            addElementEventHandler(handler);
-        }
+        this.atomicLastAccessTime.set(createTime());
     }
 
     /**
-     * @see Object#clone()
+     * Sets the isSpool attribute of the ElementAttributes object
+     * @param val The new isSpool value
      */
-    @Override
-    public IElementAttributes clone()
+    public ElementAttributes withIsSpool( boolean val )
     {
-        try
-        {
-               final ElementAttributes c = (ElementAttributes) super.clone();
-               c.setCreateTime();
-            return c;
-        }
-        catch (final CloneNotSupportedException e)
-        {
-            throw new IllegalStateException("Clone not supported. This should 
never happen.", e);
-        }
+        return new ElementAttributes(
+                val,
+                isLateral(),
+                isRemote(),
+                isEternal(),
+                maxLife(),
+                maxIdleTime(),
+                size(),
+                System.currentTimeMillis(),
+                new AtomicLong(lastAccessTime()),
+                timeFactorForMilliseconds(),
+                new ArrayList<>(elementEventHandlers()));
     }
 
     /**
-     * Gets the createTime attribute of the IAttributes object.
-     * <p>
-     * This should be the current time in milliseconds returned by the sysutem 
call when the element
-     * is put in the cache.
-     * <p>
-     * Putting an item in the cache overrides any existing items.
-     * @return The createTime value
+     * Sets the isEternal attribute of the ElementAttributes object
+     * @param val The new isEternal value
      */
-    @Override
-    public long getCreateTime()
+    public ElementAttributes withIsEternal( boolean val )
     {
-        return createTime;
+        return new ElementAttributes(
+                isSpool(),
+                isLateral(),
+                isRemote(),
+                val,
+                maxLife(),
+                maxIdleTime(),
+                size(),
+                System.currentTimeMillis(),
+                new AtomicLong(lastAccessTime()),
+                timeFactorForMilliseconds(),
+                new ArrayList<>(elementEventHandlers()));
     }
 
     /**
-     * Gets the elementEventHandlers. Returns null if none exist. Makes 
checking easy.
+     * Sets the maxLife attribute of the ElementAttributes object.
      *
-     * @return The elementEventHandlers List of IElementEventHandler objects
+     * @param mls The new MaxLifeSeconds value
      */
-    @Override
-    public ArrayList<IElementEventHandler> getElementEventHandlers()
+    public ElementAttributes withMaxLife(long mls)
     {
-        return this.eventHandlers;
+        return new ElementAttributes(
+                isSpool(),
+                isLateral(),
+                isRemote(),
+                isEternal(),
+                mls,
+                maxIdleTime(),
+                size(),
+                System.currentTimeMillis(),
+                new AtomicLong(lastAccessTime()),
+                timeFactorForMilliseconds(),
+                new ArrayList<>(elementEventHandlers()));
     }
 
     /**
-     * Gets the idleTime attribute of the IAttributes object.
-     *
-     * @return The idleTime value
+     * Sets the idleTime attribute of the ElementAttributes object. This is 
the maximum time the item can
+     * be idle in the cache, that is not accessed.
+     * <p>
+     * If this is exceeded the element will not be returned, instead it will 
be removed. It will be
+     * removed on retrieval, or removed actively if the memory shrinker is 
turned on.
+     * @param idle The new idleTime value
      */
-    @Override
-    public long getIdleTime()
+    public ElementAttributes withMaxIdleTime(long idle)
     {
-        return this.maxIdleTime;
+        return new ElementAttributes(
+                isSpool(),
+                isLateral(),
+                isRemote(),
+                isEternal(),
+                maxLife(),
+                idle,
+                size(),
+                System.currentTimeMillis(),
+                new AtomicLong(lastAccessTime()),
+                timeFactorForMilliseconds(),
+                new ArrayList<>(elementEventHandlers()));
     }
 
     /**
-     * You can turn off expiration by setting this to true. The max life value 
will be ignored.
+     * Sets the size attribute of the ElementAttributes object.
      *
-     * @return true if the item cannot expire.
+     * @param size The new size value
      */
-    @Override
-    public boolean getIsEternal()
+    public ElementAttributes withSize(int size)
     {
-        return this.IS_ETERNAL;
+        return new ElementAttributes(
+                isSpool(),
+                isLateral(),
+                isRemote(),
+                isEternal(),
+                maxLife(),
+                maxIdleTime(),
+                size,
+                System.currentTimeMillis(),
+                new AtomicLong(lastAccessTime()),
+                timeFactorForMilliseconds(),
+                new ArrayList<>(elementEventHandlers()));
     }
 
     /**
-     * Is this item laterally distributable. Can it be sent to auxiliaries of 
type lateral.
+     * Adds a ElementEventHandler. Handler's can be registered for multiple 
events. A registered
+     * handler will be called at every recognized event.
      * <p>
-     * By default this is true.
-     * @return The isLateral value
-     */
-    @Override
-    public boolean getIsLateral()
-    {
-        return this.IS_LATERAL;
-    }
-
-    /**
-     * Can this item be sent to the remote cache
-     * @return true if the item can be sent to a remote auxiliary
+     * The alternative would be to register handlers for each event. Or maybe 
The handler interface
+     * should have a method to return whether it cares about certain events.
+     *
+     * @param eventHandler The ElementEventHandler to be added to the list.
      */
     @Override
-    public boolean getIsRemote()
+    public void addElementEventHandler( final IElementEventHandler 
eventHandler )
     {
-        return this.IS_REMOTE;
+        this.elementEventHandlers.add( eventHandler );
     }
 
     /**
-     * Can this item be spooled to disk
+     * Sets the eventHandlers of the IElementAttributes object.
      * <p>
-     * By default this is true.
-     * @return The spoolable value
+     * This add the references to the local list. Subsequent changes in the 
caller's list will not
+     * be reflected.
+     *
+     * @param eventHandlers List of IElementEventHandler objects
      */
     @Override
-    public boolean getIsSpool()
+    public void addElementEventHandlers( final List<IElementEventHandler> 
eventHandlers )
     {
-        return this.IS_SPOOL;
+        if ( eventHandlers == null )
+        {
+            return;
+        }
+
+        for (final IElementEventHandler handler : eventHandlers)
+        {
+            addElementEventHandler(handler);
+        }
     }
 
     /**
@@ -268,161 +321,30 @@ public class ElementAttributes
      * @return The LastAccess value.
      */
     @Override
-    public long getLastAccessTime()
-    {
-        return this.lastAccessTime;
-    }
-
-    /**
-     * Sets the maxLife attribute of the IAttributes object. How many seconds 
it can live after
-     * creation.
-     * <p>
-     * If this is exceeded the element will not be returned, instead it will 
be removed. It will be
-     * removed on retrieval, or removed actively if the memory shrinker is 
turned on.
-     * @return The MaxLifeSeconds value
-     */
-    @Override
-    public long getMaxLife()
+    public long lastAccessTime()
     {
-        return this.maxLife;
+        return atomicLastAccessTime().get();
     }
 
     /**
-     * Gets the size attribute of the IAttributes object
-     *
-     * @return The size value
+     * Sets the LastAccessTime as now of the IElementAttributes object
      */
     @Override
-    public int getSize()
-    {
-        return size;
-    }
-
-    @Override
-    public long getTimeFactorForMilliseconds()
+    public void setLastAccessTimeNow()
     {
-        return timeFactor;
+        this.atomicLastAccessTime.set(System.currentTimeMillis());
     }
 
     /**
-     * Gets the time left to live of the IAttributes object.
+     * Gets the time left to live of the IElementAttributes object.
      * <p>
      * This is the (max life + create time) - current time.
      * @return The TimeToLiveSeconds value
      */
-    @Override
-    public long getTimeToLiveSeconds()
+    private long getTimeToLiveSeconds()
     {
         final long now = System.currentTimeMillis();
-        final long timeFactorForMilliseconds = getTimeFactorForMilliseconds();
-        return ( getCreateTime() + getMaxLife() * timeFactorForMilliseconds - 
now ) / 1000;
-    }
-
-    /**
-     * Sets the createTime attribute of the IElementAttributes object
-     */
-    public void setCreateTime()
-    {
-        createTime = System.currentTimeMillis();
-    }
-
-    /**
-     * Sets the idleTime attribute of the IAttributes object. This is the 
maximum time the item can
-     * be idle in the cache, that is not accessed.
-     * <p>
-     * If this is exceeded the element will not be returned, instead it will 
be removed. It will be
-     * removed on retrieval, or removed actively if the memory shrinker is 
turned on.
-     * @param idle The new idleTime value
-     */
-    public void setIdleTime( final long idle )
-    {
-        this.maxIdleTime = idle;
-    }
-
-    /**
-     * Sets the isEternal attribute of the ElementAttributes object. True 
means that the item should
-     * never expire. If can still be removed if it is the least recently used, 
and you are using the
-     * LRUMemory cache. it just will not be filtered for expiration by the 
cache hub.
-     *
-     * @param val The new isEternal value
-     */
-    public void setIsEternal( final boolean val )
-    {
-        this.IS_ETERNAL = val;
-    }
-
-    /**
-     * Sets the isLateral attribute of the IElementAttributes object
-     * <p>
-     * By default this is true.
-     * @param val The new isLateral value
-     */
-    public void setIsLateral( final boolean val )
-    {
-        this.IS_LATERAL = val;
-    }
-
-    /**
-     * Sets the isRemote attribute of the ElementAttributes object
-     * @param val The new isRemote value
-     */
-    public void setIsRemote( final boolean val )
-    {
-        this.IS_REMOTE = val;
-    }
-
-    /**
-     * Sets the isSpool attribute of the IElementAttributes object
-     * <p>
-     * By default this is true.
-     * @param val The new isSpool value
-     */
-    public void setIsSpool( final boolean val )
-    {
-        this.IS_SPOOL = val;
-    }
-
-    /**
-     * only for use from test code
-     */
-    public void setLastAccessTime(final long time)
-    {
-        this.lastAccessTime = time;
-    }
-
-    /**
-     * Sets the LastAccessTime as now of the IElementAttributes object
-     */
-    @Override
-    public void setLastAccessTimeNow()
-    {
-        this.lastAccessTime = System.currentTimeMillis();
-    }
-
-    /**
-     * Sets the maxLife attribute of the IAttributes object.
-     *
-     * @param mls The new MaxLifeSeconds value
-     */
-    public void setMaxLife(final long mls)
-    {
-        this.maxLife = mls;
-    }
-
-    /**
-     * Size in bytes. This is not used except in the admin pages. It will be 0 
by default
-     * and is only updated when the element is serialized.
-     *
-     * @param size The new size value
-     */
-    public void setSize( final int size )
-    {
-        this.size = size;
-    }
-
-    public void setTimeFactorForMilliseconds(final long factor)
-    {
-        this.timeFactor = factor;
+        return ( createTime() + maxLife() * timeFactorForMilliseconds() - now 
) / 1000;
     }
 
     /**
@@ -435,16 +357,16 @@ public class ElementAttributes
     {
         final StringBuilder dump = new StringBuilder();
 
-        dump.append( "[ IS_LATERAL = " ).append( IS_LATERAL );
-        dump.append( ", IS_SPOOL = " ).append( IS_SPOOL );
-        dump.append( ", IS_REMOTE = " ).append( IS_REMOTE );
-        dump.append( ", IS_ETERNAL = " ).append( IS_ETERNAL );
-        dump.append( ", MaxLifeSeconds = " ).append( getMaxLife() );
-        dump.append( ", IdleTime = " ).append( getIdleTime() );
-        dump.append( ", CreateTime = " ).append( getCreateTime() );
-        dump.append( ", LastAccessTime = " ).append( getLastAccessTime() );
-        dump.append( ", getTimeToLiveSeconds() = " ).append( String.valueOf( 
getTimeToLiveSeconds() ) );
-        dump.append( ", createTime = " ).append( String.valueOf( createTime ) 
).append( " ]" );
+        dump.append( "[ isLateral = " ).append( isLateral() );
+        dump.append( ", isSpool = " ).append( isSpool() );
+        dump.append( ", isRemote = " ).append( isRemote() );
+        dump.append( ", isEternal = " ).append( isEternal() );
+        dump.append( ", MaxLifeSeconds = " ).append( maxLife() );
+        dump.append( ", MaxIdleTime = " ).append( maxIdleTime() );
+        dump.append( ", CreateTime = " ).append( createTime() );
+        dump.append( ", LastAccessTime = " ).append( lastAccessTime() );
+        dump.append( ", getTimeToLiveSeconds() = " 
).append(getTimeToLiveSeconds());
+        dump.append( " ]" );
 
         return dump.toString();
     }
diff --git 
a/commons-jcs4-core/src/main/java/org/apache/commons/jcs4/engine/behavior/IElementAttributes.java
 
b/commons-jcs4-core/src/main/java/org/apache/commons/jcs4/engine/behavior/IElementAttributes.java
index e6c8ceee..aff279eb 100644
--- 
a/commons-jcs4-core/src/main/java/org/apache/commons/jcs4/engine/behavior/IElementAttributes.java
+++ 
b/commons-jcs4-core/src/main/java/org/apache/commons/jcs4/engine/behavior/IElementAttributes.java
@@ -30,7 +30,7 @@ import 
org.apache.commons.jcs4.engine.control.event.behavior.IElementEventHandle
  * element attributes object. It is used to track the life of the object as 
well as to restrict its
  * behavior. By default, elements get a clone of the region's attributes.
  */
-public interface IElementAttributes extends Serializable, Cloneable
+public interface IElementAttributes extends Serializable
 {
     /**
      * Adds a ElementEventHandler. Handler's can be registered for multiple 
events. A registered
@@ -46,9 +46,13 @@ public interface IElementAttributes extends Serializable, 
Cloneable
     void addElementEventHandlers( List<IElementEventHandler> eventHandlers );
 
     /**
-     * Clone object
+     * Gets the elementEventHandlers.
+     * <p>
+     * Event handlers are transient. The only events defined are in memory 
events. All handlers are
+     * lost if the item goes to disk.
+     * @return The elementEventHandlers value, null if there are none
      */
-    IElementAttributes clone();
+    ArrayList<IElementEventHandler> elementEventHandlers();
 
     /**
      * Gets the createTime attribute of the IAttributes object.
@@ -59,28 +63,19 @@ public interface IElementAttributes extends Serializable, 
Cloneable
      * Putting an item in the cache overrides any existing items.
      * @return The createTime value
      */
-    long getCreateTime();
-
-    /**
-     * Gets the elementEventHandlers.
-     * <p>
-     * Event handlers are transient. The only events defined are in memory 
events. All handlers are
-     * lost if the item goes to disk.
-     * @return The elementEventHandlers value, null if there are none
-     */
-    ArrayList<IElementEventHandler> getElementEventHandlers();
+    long createTime();
 
     /**
      * Gets the idleTime attribute of the IAttributes object
      * @return The idleTime value
      */
-    long getIdleTime();
+    long maxIdleTime();
 
     /**
      * This turns off expiration if it is true.
      * @return The IsEternal value
      */
-    boolean getIsEternal();
+    boolean isEternal();
 
     /**
      * Is this item laterally distributable. Can it be sent to auxiliaries of 
type lateral.
@@ -88,7 +83,7 @@ public interface IElementAttributes extends Serializable, 
Cloneable
      * By default this is true.
      * @return The isLateral value
      */
-    boolean getIsLateral();
+    boolean isLateral();
 
     /**
      * Can this item be sent to the remote cache.
@@ -96,7 +91,7 @@ public interface IElementAttributes extends Serializable, 
Cloneable
      * By default this is true.
      * @return The isRemote value
      */
-    boolean getIsRemote();
+    boolean isRemote();
 
     /**
      * Can this item be spooled to disk
@@ -104,14 +99,14 @@ public interface IElementAttributes extends Serializable, 
Cloneable
      * By default this is true.
      * @return The spoolable value
      */
-    boolean getIsSpool();
+    boolean isSpool();
 
     /**
      * Gets the LastAccess attribute of the IAttributes object.
      *
      * @return The LastAccess value.
      */
-    long getLastAccessTime();
+    long lastAccessTime();
 
     /**
      * Sets the maxLife attribute of the IAttributes object. How many seconds 
it can live after
@@ -121,60 +116,23 @@ public interface IElementAttributes extends Serializable, 
Cloneable
      * removed on retrieval, or removed actively if the memory shrinker is 
turned on.
      * @return The MaxLifeSeconds value
      */
-    long getMaxLife();
+    long maxLife();
 
     /**
      * Gets the size attribute of the IAttributes object
      *
      * @return The size value
      */
-    int getSize();
-
-    long getTimeFactorForMilliseconds();
+    int size();
 
     /**
-     * Gets the time left to live of the IAttributes object.
-     * <p>
-     * This is the (max life + create time) - current time.
-     * @return The TimeToLiveSeconds value
+     * Get the time factor to convert durations to milliseconds
+     * @return The time factor to convert durations to milliseconds
      */
-    long getTimeToLiveSeconds();
-
-    /**
-     * Sets the idleTime attribute of the IAttributes object. This is the 
maximum time the item can
-     * be idle in the cache, that is not accessed.
-     * <p>
-     * If this is exceeded the element will not be returned, instead it will 
be removed. It will be
-     * removed on retrieval, or removed actively if the memory shrinker is 
turned on.
-     * @param idle The new idleTime value
-     */
-    void setIdleTime( long idle );
-
-    /**
-     * Sets the isEternal attribute of the IElementAttributes object
-     * @param val The new isEternal value
-     */
-    void setIsEternal( boolean val );
+    long timeFactorForMilliseconds();
 
     /**
      * Sets the LastAccessTime as now of the IElementAttributes object
      */
     void setLastAccessTimeNow();
-
-    /**
-     * Sets the maxLife attribute of the IAttributes object.
-     *
-     * @param mls The new MaxLifeSeconds value
-     */
-    void setMaxLife(long mls);
-
-    /**
-     * Size in bytes. This is not used except in the admin pages. It will be 0 
by default
-     * and is only updated when the element is serialized.
-     *
-     * @param size The new size value
-     */
-    void setSize( int size );
-
-    void setTimeFactorForMilliseconds(long factor);
 }
diff --git 
a/commons-jcs4-core/src/main/java/org/apache/commons/jcs4/engine/control/CompositeCache.java
 
b/commons-jcs4-core/src/main/java/org/apache/commons/jcs4/engine/control/CompositeCache.java
index 78378940..dbd75c08 100644
--- 
a/commons-jcs4-core/src/main/java/org/apache/commons/jcs4/engine/control/CompositeCache.java
+++ 
b/commons-jcs4-core/src/main/java/org/apache/commons/jcs4/engine/control/CompositeCache.java
@@ -43,6 +43,7 @@ import 
org.apache.commons.jcs4.access.exception.CacheException;
 import org.apache.commons.jcs4.access.exception.ObjectNotFoundException;
 import org.apache.commons.jcs4.auxiliary.AuxiliaryCache;
 import org.apache.commons.jcs4.engine.CacheStatus;
+import org.apache.commons.jcs4.engine.ElementAttributes;
 import org.apache.commons.jcs4.engine.behavior.ICache;
 import org.apache.commons.jcs4.engine.behavior.ICacheElement;
 import org.apache.commons.jcs4.engine.behavior.ICompositeCacheAttributes;
@@ -508,7 +509,7 @@ public class CompositeCache<K, V>
     {
         if (attr != null)
         {
-            return attr.clone();
+            return new ElementAttributes(attr);
         }
         return null;
     }
@@ -966,7 +967,7 @@ public class CompositeCache<K, V>
      */
     public void handleElementEvent(final ICacheElement<K, V> element, final 
ElementEventType eventType)
     {
-        final ArrayList<IElementEventHandler> eventHandlers = 
element.getElementAttributes().getElementEventHandlers();
+        final ArrayList<IElementEventHandler> eventHandlers = 
element.getElementAttributes().elementEventHandlers();
         if (eventHandlers != null)
         {
             log.debug("Element Handlers are registered.  Create event type 
{0}", eventType);
@@ -1019,13 +1020,12 @@ public class CompositeCache<K, V>
         {
             final IElementAttributes attributes = 
element.getElementAttributes();
 
-            if (!attributes.getIsEternal())
+            if (!attributes.isEternal())
             {
                 // Remove if maxLifeSeconds exceeded
-                final long maxLifeSeconds = attributes.getMaxLife();
-                final long createTime = attributes.getCreateTime();
-
-                final long timeFactorForMilliseconds = 
attributes.getTimeFactorForMilliseconds();
+                final long maxLifeSeconds = attributes.maxLife();
+                final long createTime = attributes.createTime();
+                final long timeFactorForMilliseconds = 
attributes.timeFactorForMilliseconds();
 
                 if (maxLifeSeconds != -1 && timestamp - createTime > 
maxLifeSeconds * timeFactorForMilliseconds)
                 {
@@ -1034,8 +1034,8 @@ public class CompositeCache<K, V>
                     handleElementEvent(element, eventMaxlife);
                     return true;
                 }
-                final long idleTime = attributes.getIdleTime();
-                final long lastAccessTime = attributes.getLastAccessTime();
+                final long idleTime = attributes.maxIdleTime();
+                final long lastAccessTime = attributes.lastAccessTime();
 
                 // Remove if maxIdleTime exceeded
                 // If you have a 0 size memory cache, then the last access will
@@ -1461,7 +1461,7 @@ public class CompositeCache<K, V>
     public void spoolToDisk(final ICacheElement<K, V> ce)
     {
         // if the item is not spoolable, return
-        if (!ce.getElementAttributes().getIsSpool())
+        if (!ce.getElementAttributes().isSpool())
         {
             // there is an event defined for this.
             handleElementEvent(ce, ElementEventType.SPOOLED_NOT_ALLOWED);
@@ -1613,9 +1613,9 @@ public class CompositeCache<K, V>
                 // SEND TO REMOTE STORE
                 case REMOTE_CACHE:
                     log.debug("ce.getElementAttributes().getIsRemote() = {0}",
-                        cacheElement.getElementAttributes()::getIsRemote);
+                        cacheElement.getElementAttributes()::isRemote);
 
-                    if (cacheElement.getElementAttributes().getIsRemote() && 
!localOnly)
+                    if (cacheElement.getElementAttributes().isRemote() && 
!localOnly)
                     {
                         try
                         {
@@ -1637,7 +1637,7 @@ public class CompositeCache<K, V>
                     // lateral can't do the checking since it is dependent on 
the
                     // cache region restrictions
                     log.debug("lateralcache in aux list: cattr {0}", 
cacheAttr::useLateral);
-                    if (cacheAttr.useLateral() && 
cacheElement.getElementAttributes().getIsLateral() && !localOnly)
+                    if (cacheAttr.useLateral() && 
cacheElement.getElementAttributes().isLateral() && !localOnly)
                     {
                         // DISTRIBUTE LATERALLY
                         // Currently always multicast even if the value is
@@ -1652,7 +1652,7 @@ public class CompositeCache<K, V>
                     log.debug("diskcache in aux list: cattr {0}", 
cacheAttr::useDisk);
                     if (cacheAttr.useDisk()
                         && cacheAttr.diskUsagePattern() == 
DiskUsagePattern.UPDATE
-                        && cacheElement.getElementAttributes().getIsSpool())
+                        && cacheElement.getElementAttributes().isSpool())
                     {
                         aux.update(cacheElement);
                         log.debug("updated disk cache for {0}", 
cacheElement::getKey);
diff --git 
a/commons-jcs4-core/src/main/java/org/apache/commons/jcs4/engine/control/CompositeCacheConfigurator.java
 
b/commons-jcs4-core/src/main/java/org/apache/commons/jcs4/engine/control/CompositeCacheConfigurator.java
index 079c3935..e2b8399e 100644
--- 
a/commons-jcs4-core/src/main/java/org/apache/commons/jcs4/engine/control/CompositeCacheConfigurator.java
+++ 
b/commons-jcs4-core/src/main/java/org/apache/commons/jcs4/engine/control/CompositeCacheConfigurator.java
@@ -28,6 +28,7 @@ import 
org.apache.commons.jcs4.auxiliary.AuxiliaryCacheAttributes;
 import org.apache.commons.jcs4.auxiliary.AuxiliaryCacheConfigurator;
 import org.apache.commons.jcs4.auxiliary.AuxiliaryCacheFactory;
 import org.apache.commons.jcs4.engine.CompositeCacheAttributes;
+import org.apache.commons.jcs4.engine.ElementAttributes;
 import org.apache.commons.jcs4.engine.behavior.ICache;
 import org.apache.commons.jcs4.engine.behavior.ICompositeCacheAttributes;
 import org.apache.commons.jcs4.engine.behavior.IElementAttributes;
@@ -285,30 +286,29 @@ public class CompositeCacheConfigurator
     protected IElementAttributes parseElementAttributes( final Properties 
props, final String regName,
             final IElementAttributes defaultEAttr, final String regionPrefix )
     {
-        IElementAttributes eAttr;
-
-        final String attrName = regionPrefix + regName + 
CompositeCacheConfigurator.ELEMENT_ATTRIBUTE_PREFIX;
-
-        // auxFactory was not previously initialized.
-        // String prefix = regionPrefix + regName + ATTRIBUTE_PREFIX;
-        eAttr = OptionConverter.instantiateByKey( props, attrName, null );
-        if ( eAttr == null )
+        final String prefix = regionPrefix + regName + 
ELEMENT_ATTRIBUTE_PREFIX;
+        Class<? extends IElementAttributes> elementClass = 
OptionConverter.findClassByKey(props, prefix);
+        if (elementClass == null)
         {
-            log.info( "No special ElementAttribute class defined for key 
[{0}], "
-                    + "using default class.", attrName );
-
-            eAttr = defaultEAttr;
+            if (defaultEAttr != null)
+            {
+                elementClass = defaultEAttr.getClass();
+            }
+            else
+            {
+                elementClass = ElementAttributes.class;
+            }
+            log.debug("Using default element attributes class for region 
\"{0}\": {1}",
+                    regName, elementClass.getName());
         }
 
-        log.debug( "Parsing options for \"{0}\"", attrName );
+        log.debug( "Parsing options for \"{0}\"", prefix );
 
-        PropertySetter.setProperties( eAttr, props, attrName + "." );
-        // eAttr.setCacheName( regName );
-
-        log.debug( "End of parsing for \"{0}\"", attrName );
+        IElementAttributes eAttr = ConfigurationBuilder.create(elementClass, 
defaultEAttr)
+                .fromProperties(props, prefix)
+                .build();
 
-        // GET CACHE FROM FACTORY WITH ATTRIBUTES
-        // eAttr.setCacheName( regName );
+        log.debug( "End of parsing for \"{0}\"", prefix );
         return eAttr;
     }
 
diff --git 
a/commons-jcs4-core/src/main/java/org/apache/commons/jcs4/engine/control/CompositeCacheManager.java
 
b/commons-jcs4-core/src/main/java/org/apache/commons/jcs4/engine/control/CompositeCacheManager.java
index 7587d767..b139178a 100644
--- 
a/commons-jcs4-core/src/main/java/org/apache/commons/jcs4/engine/control/CompositeCacheManager.java
+++ 
b/commons-jcs4-core/src/main/java/org/apache/commons/jcs4/engine/control/CompositeCacheManager.java
@@ -613,7 +613,7 @@ public class CompositeCacheManager
      */
     public IElementAttributes getDefaultElementAttributes()
     {
-        return this.defaultElementAttr.clone();
+        return new ElementAttributes(this.defaultElementAttr);
     }
 
     /**
diff --git 
a/commons-jcs4-core/src/main/java/org/apache/commons/jcs4/engine/memory/shrinking/ShrinkerThread.java
 
b/commons-jcs4-core/src/main/java/org/apache/commons/jcs4/engine/memory/shrinking/ShrinkerThread.java
index 43437d42..701a1d1c 100644
--- 
a/commons-jcs4-core/src/main/java/org/apache/commons/jcs4/engine/memory/shrinking/ShrinkerThread.java
+++ 
b/commons-jcs4-core/src/main/java/org/apache/commons/jcs4/engine/memory/shrinking/ShrinkerThread.java
@@ -133,7 +133,7 @@ public class ShrinkerThread<K, V>
 
                 // If the element is not eternal, check if it should be
                 // removed and remove it if so.
-                if ( !attributes.getIsEternal() )
+                if ( !attributes.isEternal() )
                 {
                     remove = cache.isExpired( cacheElement, now,
                             ElementEventType.EXCEEDED_MAXLIFE_BACKGROUND,
@@ -152,7 +152,7 @@ public class ShrinkerThread<K, V>
                 {
                     if ( !spoolLimit || spoolCount < this.maxSpoolPerRun )
                     {
-                        final long lastAccessTime = 
attributes.getLastAccessTime();
+                        final long lastAccessTime = 
attributes.lastAccessTime();
 
                         if ( lastAccessTime + maxMemoryIdleTime < now )
                         {
diff --git 
a/commons-jcs4-core/src/main/java/org/apache/commons/jcs4/utils/serialization/SerializationConversionUtil.java
 
b/commons-jcs4-core/src/main/java/org/apache/commons/jcs4/utils/serialization/SerializationConversionUtil.java
index 41a921e2..b057ab82 100644
--- 
a/commons-jcs4-core/src/main/java/org/apache/commons/jcs4/utils/serialization/SerializationConversionUtil.java
+++ 
b/commons-jcs4-core/src/main/java/org/apache/commons/jcs4/utils/serialization/SerializationConversionUtil.java
@@ -23,8 +23,10 @@ import java.io.IOException;
 
 import org.apache.commons.jcs4.engine.CacheElement;
 import org.apache.commons.jcs4.engine.CacheElementSerialized;
+import org.apache.commons.jcs4.engine.ElementAttributes;
 import org.apache.commons.jcs4.engine.behavior.ICacheElement;
 import org.apache.commons.jcs4.engine.behavior.ICacheElementSerialized;
+import org.apache.commons.jcs4.engine.behavior.IElementAttributes;
 import org.apache.commons.jcs4.engine.behavior.IElementSerializer;
 import org.apache.commons.jcs4.log.Log;
 
@@ -115,7 +117,9 @@ public class SerializationConversionUtil
                 serializedValue = 
elementSerializer.serialize(element.getVal());
 
                 // update size in bytes
-                element.getElementAttributes().setSize(serializedValue.length);
+                IElementAttributes copy = new 
ElementAttributes(element.getElementAttributes())
+                        .withSize(serializedValue.length);
+                element.setElementAttributes(copy);
             }
             catch ( final IOException e )
             {
diff --git a/commons-jcs4-core/src/test/conf/cache.ccf 
b/commons-jcs4-core/src/test/conf/cache.ccf
index f5fbcb89..cfc3fc5e 100644
--- a/commons-jcs4-core/src/test/conf/cache.ccf
+++ b/commons-jcs4-core/src/test/conf/cache.ccf
@@ -27,7 +27,7 @@ jcs.default.cacheattributes.ShrinkerIntervalSeconds=60
 jcs.default.elementattributes=org.apache.commons.jcs4.engine.ElementAttributes
 jcs.default.elementattributes.IsEternal=false
 jcs.default.elementattributes.MaxLife=700
-jcs.default.elementattributes.IdleTime=1800
+jcs.default.elementattributes.MaxIdleTime=1800
 jcs.default.elementattributes.IsSpool=true
 jcs.default.elementattributes.IsRemote=true
 jcs.default.elementattributes.IsLateral=true
diff --git a/commons-jcs4-core/src/test/conf/remote.cache.ccf 
b/commons-jcs4-core/src/test/conf/remote.cache.ccf
index d051dc9a..987091bc 100644
--- a/commons-jcs4-core/src/test/conf/remote.cache.ccf
+++ b/commons-jcs4-core/src/test/conf/remote.cache.ccf
@@ -37,7 +37,7 @@ jcs.default.cacheattributes.ShrinkerIntervalSeconds=60
 jcs.default.elementattributes=org.apache.commons.jcs4.engine.ElementAttributes
 jcs.default.elementattributes.IsEternal=false
 jcs.default.elementattributes.MaxLife=7000
-jcs.default.elementattributes.IdleTime=1800
+jcs.default.elementattributes.MaxIdleTime=1800
 jcs.default.elementattributes.IsSpool=true
 jcs.default.elementattributes.IsRemote=true
 jcs.default.elementattributes.IsLateral=true
diff --git 
a/commons-jcs4-core/src/test/java/org/apache/commons/jcs4/JCSCacheElementRetrievalUnitTest.java
 
b/commons-jcs4-core/src/test/java/org/apache/commons/jcs4/JCSCacheElementRetrievalUnitTest.java
index ac6a8ae6..b8da68d0 100644
--- 
a/commons-jcs4-core/src/test/java/org/apache/commons/jcs4/JCSCacheElementRetrievalUnitTest.java
+++ 
b/commons-jcs4-core/src/test/java/org/apache/commons/jcs4/JCSCacheElementRetrievalUnitTest.java
@@ -46,7 +46,7 @@ class JCSCacheElementRetrievalUnitTest
         final ICacheElement<String, String> elem = jcs.getCacheElement( 
"test_key" );
         assertEquals( "testCache1", elem.getCacheName(), "Name wasn't right" );
 
-        final long diff = now - elem.getElementAttributes().getCreateTime();
+        final long diff = now - elem.getElementAttributes().createTime();
         assertTrue( diff >= 0, "Create time should have been at or after the 
call" );
 
     }
diff --git 
a/commons-jcs4-core/src/test/java/org/apache/commons/jcs4/access/CacheAccessUnitTest.java
 
b/commons-jcs4-core/src/test/java/org/apache/commons/jcs4/access/CacheAccessUnitTest.java
index 4ad990e8..5bd4028d 100644
--- 
a/commons-jcs4-core/src/test/java/org/apache/commons/jcs4/access/CacheAccessUnitTest.java
+++ 
b/commons-jcs4-core/src/test/java/org/apache/commons/jcs4/access/CacheAccessUnitTest.java
@@ -114,8 +114,8 @@ class CacheAccessUnitTest
                 .withMaxObjects(maxMemorySize);
 
         final long maxLife = 9876;
-        final ElementAttributes attr = new ElementAttributes();
-        attr.setMaxLife(maxLife);
+        final ElementAttributes attr = ElementAttributes.defaults()
+                .withMaxLife(maxLife);
 
         final CacheAccess<String, Integer> access = JCS.getInstance( 
"testGetMatching_Normal", cattr, attr );
 
@@ -169,8 +169,8 @@ class CacheAccessUnitTest
                 .withMaxObjects(maxMemorySize);
 
         final long maxLife = 9876;
-        final ElementAttributes attr = new ElementAttributes();
-        attr.setMaxLife(maxLife);
+        final ElementAttributes attr = ElementAttributes.defaults()
+                .withMaxLife(maxLife);
 
         final CacheAccess<String, Integer> access = JCS.getInstance( 
"testGetMatching_Normal", cattr, attr );
 
@@ -334,8 +334,8 @@ class CacheAccessUnitTest
                 .withMaxMemoryIdleTimeSeconds(maxIdleTime);
 
         final long maxLife = 9876;
-        final ElementAttributes attr = new ElementAttributes();
-        attr.setMaxLife(maxLife);
+        final ElementAttributes attr = ElementAttributes.defaults()
+                .withMaxLife(maxLife);
 
         final CacheAccess<String, String> access = JCS.getInstance( 
"testRegionDefinitonWithAttributes", ca, attr );
         assertNotNull( access, "We should have an access class" );
@@ -356,13 +356,13 @@ class CacheAccessUnitTest
         assertNotNull( access, "We should have an access class" );
 
         final long maxLife = 9876;
-        final ElementAttributes attr = new ElementAttributes();
-        attr.setMaxLife(maxLife);
+        final ElementAttributes attr = ElementAttributes.defaults()
+                .withMaxLife(maxLife);
 
         access.setDefaultElementAttributes( attr );
 
-        assertEquals( attr.getMaxLife(), access.getDefaultElementAttributes()
-            .getMaxLife(), "Wrong element attributes." );
+        assertEquals( attr.maxLife(), access.getDefaultElementAttributes()
+            .maxLife(), "Wrong element attributes." );
 
         final String key = "mykey";
         final String value = "myvalue";
@@ -372,6 +372,6 @@ class CacheAccessUnitTest
         final ICacheElement<String, String> element = access.getCacheElement( 
key );
 
         assertEquals( maxLife, element.getElementAttributes()
-            .getMaxLife(), "Wrong max life.  Should have the new value." );
+            .maxLife(), "Wrong max life.  Should have the new value." );
     }
 }
diff --git 
a/commons-jcs4-core/src/test/java/org/apache/commons/jcs4/access/TestCacheAccess.java
 
b/commons-jcs4-core/src/test/java/org/apache/commons/jcs4/access/TestCacheAccess.java
index be7ea21f..7ac9cf36 100644
--- 
a/commons-jcs4-core/src/test/java/org/apache/commons/jcs4/access/TestCacheAccess.java
+++ 
b/commons-jcs4-core/src/test/java/org/apache/commons/jcs4/access/TestCacheAccess.java
@@ -898,7 +898,7 @@ public class TestCacheAccess
                         final long n_start = System.currentTimeMillis();
                         for ( int n = 0; n < num; n++ )
                         {
-                            attrp.clone();
+                            new ElementAttributes(attrp);
                         }
                         final long n_end = System.currentTimeMillis();
                         p( "---cloned attr " + num + " in " + String.valueOf( 
n_end - n_start ) + " millis ---" );
diff --git 
a/commons-jcs4-core/src/test/java/org/apache/commons/jcs4/auxiliary/disk/block/AbstractBlockDiskCacheUnitTest.java
 
b/commons-jcs4-core/src/test/java/org/apache/commons/jcs4/auxiliary/disk/block/AbstractBlockDiskCacheUnitTest.java
index 71e7d59f..e5b2e636 100644
--- 
a/commons-jcs4-core/src/test/java/org/apache/commons/jcs4/auxiliary/disk/block/AbstractBlockDiskCacheUnitTest.java
+++ 
b/commons-jcs4-core/src/test/java/org/apache/commons/jcs4/auxiliary/disk/block/AbstractBlockDiskCacheUnitTest.java
@@ -336,8 +336,8 @@ public abstract class AbstractBlockDiskCacheUnitTest{
             final CacheElement<GroupAttrName<String>, String> element = new 
CacheElement<>(cacheName,
                 groupAttrName, "data:" + i);
 
-            final ElementAttributes eAttr = new ElementAttributes();
-            eAttr.setIsSpool(true);
+            final ElementAttributes eAttr = ElementAttributes.defaults()
+                    .withIsSpool(true);
             element.setElementAttributes(eAttr);
 
             disk.processUpdate(element);
@@ -386,8 +386,8 @@ public abstract class AbstractBlockDiskCacheUnitTest{
         final int cnt = 25;
         for (int i = 0; i < cnt; i++)
         {
-            final ElementAttributes eAttr = new ElementAttributes();
-            eAttr.setIsSpool(true);
+            final ElementAttributes eAttr = ElementAttributes.defaults()
+                    .withIsSpool(true);
             final ICacheElement<String, String> element = new 
CacheElement<>("testRemove_PartialKey", i + ":key", "data:"
                 + i);
             element.setElementAttributes(eAttr);
@@ -429,8 +429,8 @@ public abstract class AbstractBlockDiskCacheUnitTest{
         final int cnt = 25;
         for (int i = 0; i < cnt; i++)
         {
-            final ElementAttributes eAttr = new ElementAttributes();
-            eAttr.setIsSpool(true);
+            final ElementAttributes eAttr = ElementAttributes.defaults()
+                    .withIsSpool(true);
             final ICacheElement<String, String> element = new 
CacheElement<>("testRemoveItems", "key:" + i, "data:" + i);
             element.setElementAttributes(eAttr);
             disk.processUpdate(element);
diff --git 
a/commons-jcs4-core/src/test/java/org/apache/commons/jcs4/auxiliary/disk/indexed/AbstractIndexDiskCacheUnitTest.java
 
b/commons-jcs4-core/src/test/java/org/apache/commons/jcs4/auxiliary/disk/indexed/AbstractIndexDiskCacheUnitTest.java
index cb936df9..af3cc81a 100644
--- 
a/commons-jcs4-core/src/test/java/org/apache/commons/jcs4/auxiliary/disk/indexed/AbstractIndexDiskCacheUnitTest.java
+++ 
b/commons-jcs4-core/src/test/java/org/apache/commons/jcs4/auxiliary/disk/indexed/AbstractIndexDiskCacheUnitTest.java
@@ -704,8 +704,8 @@ public abstract class AbstractIndexDiskCacheUnitTest{
             final CacheElement<GroupAttrName<String>, String> element = new 
CacheElement<>(cacheName,
                 groupAttrName, "data:" + i);
 
-            final ElementAttributes eAttr = new ElementAttributes();
-            eAttr.setIsSpool(true);
+            final ElementAttributes eAttr = ElementAttributes.defaults()
+                    .withIsSpool(true);
             element.setElementAttributes(eAttr);
 
             disk.processUpdate(element);
@@ -754,8 +754,8 @@ public abstract class AbstractIndexDiskCacheUnitTest{
         final int cnt = 25;
         for (int i = 0; i < cnt; i++)
         {
-            final ElementAttributes eAttr = new ElementAttributes();
-            eAttr.setIsSpool(true);
+            final ElementAttributes eAttr = ElementAttributes.defaults()
+                    .withIsSpool(true);
             final ICacheElement<String, String> element = new 
CacheElement<>("testRemove_PartialKey", i + ":key", "data:"
                 + i);
             element.setElementAttributes(eAttr);
@@ -828,8 +828,8 @@ public abstract class AbstractIndexDiskCacheUnitTest{
         final int cnt = 25;
         for (int i = 0; i < cnt; i++)
         {
-            final ElementAttributes eAttr = new ElementAttributes();
-            eAttr.setIsSpool(true);
+            final ElementAttributes eAttr = ElementAttributes.defaults()
+                    .withIsSpool(true);
             final ICacheElement<String, String> element = new 
CacheElement<>("testRemoveItems", "key:" + i, "data:" + i);
             element.setElementAttributes(eAttr);
             disk.processUpdate(element);
@@ -863,8 +863,8 @@ public abstract class AbstractIndexDiskCacheUnitTest{
         final int cnt = 999;
         for (int i = 0; i < cnt; i++)
         {
-            final ElementAttributes eAttr = new ElementAttributes();
-            eAttr.setIsSpool(true);
+            final ElementAttributes eAttr = ElementAttributes.defaults()
+                    .withIsSpool(true);
             final ICacheElement<String, String> element = new 
CacheElement<>("testSimplePutAndGet", "key:" + i, "data:" + i);
             element.setElementAttributes(eAttr);
             disk.processUpdate(element);
diff --git 
a/commons-jcs4-core/src/test/java/org/apache/commons/jcs4/auxiliary/disk/indexed/IndexedDiskCacheKeyStoreUnitTest.java
 
b/commons-jcs4-core/src/test/java/org/apache/commons/jcs4/auxiliary/disk/indexed/IndexedDiskCacheKeyStoreUnitTest.java
index 01fd5636..6380c594 100644
--- 
a/commons-jcs4-core/src/test/java/org/apache/commons/jcs4/auxiliary/disk/indexed/IndexedDiskCacheKeyStoreUnitTest.java
+++ 
b/commons-jcs4-core/src/test/java/org/apache/commons/jcs4/auxiliary/disk/indexed/IndexedDiskCacheKeyStoreUnitTest.java
@@ -56,8 +56,8 @@ class IndexedDiskCacheKeyStoreUnitTest
         final int cnt = 25;
         for ( int i = 0; i < cnt; i++ )
         {
-            final ElementAttributes eAttr = new ElementAttributes();
-            eAttr.setIsSpool( true );
+            final ElementAttributes eAttr = ElementAttributes.defaults()
+                    .withIsSpool(true);
             final ICacheElement<String, String> element = new CacheElement<>( 
cattr.getCacheName(), "key:" + i, "data:" + i );
             element.setElementAttributes( eAttr );
             disk.processUpdate( element );
@@ -65,8 +65,8 @@ class IndexedDiskCacheKeyStoreUnitTest
 
         final long preAddRemoveSize = disk.getDataFileSize();
 
-        final ElementAttributes eAttr = new ElementAttributes();
-        eAttr.setIsSpool( true );
+        final ElementAttributes eAttr = ElementAttributes.defaults()
+                .withIsSpool(true);
         final ICacheElement<String, String> elementSetup = new CacheElement<>( 
cattr.getCacheName(), "key:A", "data:A" );
         elementSetup.setElementAttributes( eAttr );
         disk.processUpdate( elementSetup );
@@ -115,8 +115,8 @@ class IndexedDiskCacheKeyStoreUnitTest
         final int cnt = 25;
         for ( int i = 0; i < cnt; i++ )
         {
-            final ElementAttributes eAttr = new ElementAttributes();
-            eAttr.setIsSpool( true );
+            final ElementAttributes eAttr = ElementAttributes.defaults()
+                    .withIsSpool(true);
             final ICacheElement<String, String> element = new CacheElement<>( 
cattr.getCacheName(), "key:" + i, "data:" + i );
             element.setElementAttributes( eAttr );
             disk.processUpdate( element );
diff --git 
a/commons-jcs4-core/src/test/java/org/apache/commons/jcs4/auxiliary/remote/RemoteCacheListenerUnitTest.java
 
b/commons-jcs4-core/src/test/java/org/apache/commons/jcs4/auxiliary/remote/RemoteCacheListenerUnitTest.java
index fa8383db..50ba4c51 100644
--- 
a/commons-jcs4-core/src/test/java/org/apache/commons/jcs4/auxiliary/remote/RemoteCacheListenerUnitTest.java
+++ 
b/commons-jcs4-core/src/test/java/org/apache/commons/jcs4/auxiliary/remote/RemoteCacheListenerUnitTest.java
@@ -59,8 +59,8 @@ class RemoteCacheListenerUnitTest
         final String cacheName = "testName";
         final String key = "key";
         final String value = "value fdsadf dsafdsa fdsaf dsafdsaf dsafdsaf 
dsaf dsaf dsaf dsafa dsaf dsaf dsafdsaf";
-        final ElementAttributes attr = new ElementAttributes();
-        attr.setMaxLife(34);
+        final ElementAttributes attr = ElementAttributes.defaults()
+                .withMaxLife(34);
 
         final IElementSerializer elementSerializer = new StandardSerializer();
 
@@ -77,8 +77,8 @@ class RemoteCacheListenerUnitTest
 
         assertNotNull( after, "Should have a deserialized object." );
         assertEquals( value, after.getVal(), "Values should be the same." );
-        assertEquals( attr.getMaxLife(), after
-            .getElementAttributes().getMaxLife(), "Attributes should be the 
same." );
+        assertEquals( attr.maxLife(), after
+            .getElementAttributes().maxLife(), "Attributes should be the 
same." );
         assertEquals( key, after.getKey(), "Keys should be the same." );
         assertEquals( cacheName, after.getCacheName(), "Cache name should be 
the same." );
     }
@@ -103,8 +103,8 @@ class RemoteCacheListenerUnitTest
         final String cacheName = "testName";
         final String key = "key";
         final String value = "value fdsadf dsafdsa fdsaf dsafdsaf dsafdsaf 
dsaf dsaf dsaf dsafa dsaf dsaf dsafdsaf";
-        final ElementAttributes attr = new ElementAttributes();
-        attr.setMaxLife(34);
+        final ElementAttributes attr = ElementAttributes.defaults()
+                .withMaxLife(34);
 
         final IElementSerializer elementSerializer = new StandardSerializer();
 
diff --git 
a/commons-jcs4-core/src/test/java/org/apache/commons/jcs4/engine/ElementAttributesUtils.java
 
b/commons-jcs4-core/src/test/java/org/apache/commons/jcs4/engine/ElementAttributesUtils.java
index f12aeefe..e6671729 100644
--- 
a/commons-jcs4-core/src/test/java/org/apache/commons/jcs4/engine/ElementAttributesUtils.java
+++ 
b/commons-jcs4-core/src/test/java/org/apache/commons/jcs4/engine/ElementAttributesUtils.java
@@ -23,7 +23,8 @@ package org.apache.commons.jcs4.engine;
  * Allow test access to set last access time without exposing public method
  */
 public class ElementAttributesUtils {
-    public static void setLastAccessTime(final ElementAttributes ea, final 
long time) {
-        ea.setLastAccessTime(time);
+    public static void setLastAccessTime(final ElementAttributes ea, final 
long time)
+    {
+        ea.atomicLastAccessTime().set(time);
     }
 }
diff --git 
a/commons-jcs4-core/src/test/java/org/apache/commons/jcs4/engine/control/event/SimpleEventHandlingUnitTest.java
 
b/commons-jcs4-core/src/test/java/org/apache/commons/jcs4/engine/control/event/SimpleEventHandlingUnitTest.java
index 81bd81eb..5443c5cf 100644
--- 
a/commons-jcs4-core/src/test/java/org/apache/commons/jcs4/engine/control/event/SimpleEventHandlingUnitTest.java
+++ 
b/commons-jcs4-core/src/test/java/org/apache/commons/jcs4/engine/control/event/SimpleEventHandlingUnitTest.java
@@ -166,12 +166,12 @@ class SimpleEventHandlingUnitTest
         throws Exception
     {
        final ElementAttributes elem1 = new ElementAttributes();
-       final long ctime1 = elem1.getCreateTime();
+       final long ctime1 = elem1.createTime();
 
        Thread.sleep(10);
 
-       final IElementAttributes elem2 = elem1.clone();
-       final long ctime2 = elem2.getCreateTime();
+       final IElementAttributes elem2 = new ElementAttributes(elem1);
+       final long ctime2 = elem2.createTime();
 
         assertFalse( ctime1 == ctime2, "Creation times should be different" );
     }
@@ -204,7 +204,7 @@ class SimpleEventHandlingUnitTest
         }
 
         // wait a bit for the items to expire
-        Thread.sleep(attributes.getIdleTime() * 1000 + 100);
+        Thread.sleep(attributes.maxIdleTime() * 1000 + 100);
 
         for ( int i = 0; i < 200; i++ )
         {
@@ -242,7 +242,7 @@ class SimpleEventHandlingUnitTest
         }
 
         // wait a bit for the items to expire
-        Thread.sleep(attributes.getMaxLife() * 1000 + 100);
+        Thread.sleep(attributes.maxLife() * 1000 + 100);
 
         for ( int i = 0; i < 200; i++ )
         {
diff --git 
a/commons-jcs4-core/src/test/java/org/apache/commons/jcs4/engine/memory/shrinking/ShrinkerThreadUnitTest.java
 
b/commons-jcs4-core/src/test/java/org/apache/commons/jcs4/engine/memory/shrinking/ShrinkerThreadUnitTest.java
index 76fea7c1..6f50e06b 100644
--- 
a/commons-jcs4-core/src/test/java/org/apache/commons/jcs4/engine/memory/shrinking/ShrinkerThreadUnitTest.java
+++ 
b/commons-jcs4-core/src/test/java/org/apache/commons/jcs4/engine/memory/shrinking/ShrinkerThreadUnitTest.java
@@ -61,11 +61,10 @@ class ShrinkerThreadUnitTest
         final String key = "key";
         final String value = "value";
 
-        final ICacheElement<String, String> element = new CacheElement<>( 
"testRegion", key, value );
-        final ElementAttributes elementAttr = new ElementAttributes();
-        elementAttr.setIsEternal( false );
-        elementAttr.setMaxLife(1);
-        element.setElementAttributes( elementAttr );
+        final ICacheElement<String, String> element = new CacheElement<>( 
"testRegion", key, value,
+                new ElementAttributes()
+                .withIsEternal(false)
+                .withMaxLife(1));
 
         long now = System.currentTimeMillis();
         // add two seconds
@@ -98,12 +97,11 @@ class ShrinkerThreadUnitTest
         final String key = "key";
         final String value = "value";
 
-        final ICacheElement<String, String> element = new CacheElement<>( 
"testRegion", key, value );
-        final ElementAttributes elementAttr = new ElementAttributes();
-        elementAttr.setIsEternal( false );
-        elementAttr.setMaxLife(100);
-        elementAttr.setIdleTime(1);
-        element.setElementAttributes( elementAttr );
+        final ICacheElement<String, String> element = new CacheElement<>( 
"testRegion", key, value,
+                new ElementAttributes()
+                .withIsEternal(false)
+                .withMaxLife(100)
+                .withMaxIdleTime(1));
 
         long now = System.currentTimeMillis();
         // add two seconds
@@ -136,11 +134,10 @@ class ShrinkerThreadUnitTest
         final String key = "key";
         final String value = "value";
 
-        final ICacheElement<String, String> element = new CacheElement<>( 
"testRegion", key, value );
-        final ElementAttributes elementAttr = new ElementAttributes();
-        elementAttr.setIsEternal( false );
-        elementAttr.setMaxLife(1);
-        element.setElementAttributes( elementAttr );
+        final ICacheElement<String, String> element = new CacheElement<>( 
"testRegion", key, value,
+                new ElementAttributes()
+                .withIsEternal(false)
+                .withMaxLife(1));
 
         long now = System.currentTimeMillis();
         // subtract two seconds
@@ -173,12 +170,11 @@ class ShrinkerThreadUnitTest
         final String key = "key";
         final String value = "value";
 
-        final ICacheElement<String, String> element = new CacheElement<>( 
"testRegion", key, value );
-        final ElementAttributes elementAttr = new ElementAttributes();
-        elementAttr.setIsEternal( false );
-        elementAttr.setMaxLife(100);
-        elementAttr.setIdleTime(1);
-        element.setElementAttributes( elementAttr );
+        final ICacheElement<String, String> element = new CacheElement<>( 
"testRegion", key, value,
+                new ElementAttributes()
+                .withIsEternal(false)
+                .withMaxLife(100)
+                .withMaxIdleTime(1));
 
         long now = System.currentTimeMillis();
         // subtract two seconds
@@ -218,9 +214,9 @@ class ShrinkerThreadUnitTest
 
         final ICacheElement<String, String> element = new CacheElement<>( 
"testRegion", key, value );
 
-        final ElementAttributes elementAttr = new ElementAttributes();
-        elementAttr.setIsEternal( false );
-        elementAttr.setMaxLife(1);
+        final ElementAttributes elementAttr = new ElementAttributes()
+                .withIsEternal(false)
+                .withMaxLife(1);
         element.setElementAttributes( elementAttr );
         memory.update( element );
 
@@ -268,9 +264,9 @@ class ShrinkerThreadUnitTest
 
             final ICacheElement<String, String> element = new CacheElement<>( 
"testRegion", key, value );
 
-            final ElementAttributes elementAttr = new ElementAttributes();
-            elementAttr.setIsEternal( false );
-            elementAttr.setMaxLife(1);
+            final ElementAttributes elementAttr = new ElementAttributes()
+                    .withIsEternal(false)
+                    .withMaxLife(1);
             element.setElementAttributes( elementAttr );
             memory.update( element );
 
@@ -321,10 +317,10 @@ class ShrinkerThreadUnitTest
 
             final ICacheElement<String, String> element = new CacheElement<>( 
"testRegion", key, value );
 
-            final ElementAttributes elementAttr = new ElementAttributes();
+            final ElementAttributes elementAttr = new ElementAttributes()
+                    .withIsEternal(false)
+                    .withMaxLife(1);
             elementAttr.addElementEventHandler( handler );
-            elementAttr.setIsEternal( false );
-            elementAttr.setMaxLife(1);
             element.setElementAttributes( elementAttr );
             memory.update( element );
 
diff --git 
a/commons-jcs4-core/src/test/java/org/apache/commons/jcs4/utils/serialization/SerializationConversionUtilUnitTest.java
 
b/commons-jcs4-core/src/test/java/org/apache/commons/jcs4/utils/serialization/SerializationConversionUtilUnitTest.java
index c2fc877e..002f1518 100644
--- 
a/commons-jcs4-core/src/test/java/org/apache/commons/jcs4/utils/serialization/SerializationConversionUtilUnitTest.java
+++ 
b/commons-jcs4-core/src/test/java/org/apache/commons/jcs4/utils/serialization/SerializationConversionUtilUnitTest.java
@@ -54,8 +54,8 @@ class SerializationConversionUtilUnitTest
 
         final IElementSerializer elementSerializer = new StandardSerializer();
 
-        final ElementAttributes attr = new ElementAttributes();
-        attr.setMaxLife(34);
+        final ElementAttributes attr = ElementAttributes.defaults()
+                .withMaxLife(34);
 
         final ICacheElement<String, String> before = new CacheElement<>( 
cacheName, key, value );
         before.setElementAttributes( attr );
@@ -76,8 +76,8 @@ class SerializationConversionUtilUnitTest
         // VERIFY
         assertNotNull( after, "Should have a deserialized object." );
         assertEquals( before.getVal(), after.getVal(), "Values should be the 
same." );
-        assertEquals( before.getElementAttributes().getMaxLife(), after
-            .getElementAttributes().getMaxLife(), "Attributes should be the 
same." );
+        assertEquals( before.getElementAttributes().maxLife(), after
+            .getElementAttributes().maxLife(), "Attributes should be the 
same." );
         assertEquals( before.getKey(), after.getKey(), "Keys should be the 
same." );
         assertEquals( before.getCacheName(), after.getCacheName(), "Cache name 
should be the same." );
     }
@@ -137,8 +137,8 @@ class SerializationConversionUtilUnitTest
 
         final IElementSerializer elementSerializer = null; // new 
StandardSerializer();
 
-        final ElementAttributes attr = new ElementAttributes();
-        attr.setMaxLife(34);
+        final ElementAttributes attr = ElementAttributes.defaults()
+                .withMaxLife(34);
 
         final ICacheElement<String, String> before = new CacheElement<>( 
cacheName, key, value );
         before.setElementAttributes( attr );
@@ -173,8 +173,8 @@ class SerializationConversionUtilUnitTest
 
         final IElementSerializer elementSerializer = new StandardSerializer();
 
-        final ElementAttributes attr = new ElementAttributes();
-        attr.setMaxLife(34);
+        final ElementAttributes attr = ElementAttributes.defaults()
+                .withMaxLife(34);
 
         final ICacheElement<String, String> before = new CacheElement<>( 
cacheName, key, value );
         before.setElementAttributes( attr );
@@ -193,8 +193,8 @@ class SerializationConversionUtilUnitTest
         // VERIFY
         assertNotNull( after, "Should have a deserialized object." );
         assertEquals( before.getVal(), after.getVal(), "Values should be the 
same." );
-        assertEquals( before.getElementAttributes().getMaxLife(), after
-            .getElementAttributes().getMaxLife(), "Attributes should be the 
same." );
+        assertEquals( before.getElementAttributes().maxLife(), after
+            .getElementAttributes().maxLife(), "Attributes should be the 
same." );
         assertEquals( before.getKey(), after.getKey(), "Keys should be the 
same." );
         assertEquals( before.getCacheName(), after.getCacheName(), "Cache name 
should be the same." );
     }
diff --git a/commons-jcs4-core/src/test/test-conf/TestHSQLDiskCache.ccf 
b/commons-jcs4-core/src/test/test-conf/TestHSQLDiskCache.ccf
index f71839bc..0340adbf 100644
--- a/commons-jcs4-core/src/test/test-conf/TestHSQLDiskCache.ccf
+++ b/commons-jcs4-core/src/test/test-conf/TestHSQLDiskCache.ccf
@@ -27,7 +27,7 @@ jcs.default.cacheattributes.ShrinkerIntervalSeconds=60
 jcs.default.elementattributes=org.apache.commons.jcs4.engine.ElementAttributes
 jcs.default.elementattributes.IsEternal=false
 jcs.default.elementattributes.MaxLife=700
-jcs.default.elementattributes.IdleTime=1800
+jcs.default.elementattributes.MaxIdleTime=1800
 jcs.default.elementattributes.IsSpool=true
 jcs.default.elementattributes.IsRemote=true
 jcs.default.elementattributes.IsLateral=true
diff --git 
a/commons-jcs4-core/src/test/test-conf/TestHSQLDiskCacheConcurrent.ccf 
b/commons-jcs4-core/src/test/test-conf/TestHSQLDiskCacheConcurrent.ccf
index 9f5efcc6..f022d427 100644
--- a/commons-jcs4-core/src/test/test-conf/TestHSQLDiskCacheConcurrent.ccf
+++ b/commons-jcs4-core/src/test/test-conf/TestHSQLDiskCacheConcurrent.ccf
@@ -27,7 +27,7 @@ jcs.default.cacheattributes.ShrinkerIntervalSeconds=60
 jcs.default.elementattributes=org.apache.commons.jcs4.engine.ElementAttributes
 jcs.default.elementattributes.IsEternal=false
 jcs.default.elementattributes.MaxLife=700
-jcs.default.elementattributes.IdleTime=1800
+jcs.default.elementattributes.MaxIdleTime=1800
 jcs.default.elementattributes.IsSpool=true
 jcs.default.elementattributes.IsRemote=true
 jcs.default.elementattributes.IsLateral=true
diff --git a/commons-jcs4-core/src/test/test-conf/TestJDBCDiskCache.ccf 
b/commons-jcs4-core/src/test/test-conf/TestJDBCDiskCache.ccf
index 42d3d6e7..235de35b 100644
--- a/commons-jcs4-core/src/test/test-conf/TestJDBCDiskCache.ccf
+++ b/commons-jcs4-core/src/test/test-conf/TestJDBCDiskCache.ccf
@@ -27,7 +27,7 @@ jcs.default.cacheattributes.ShrinkerIntervalSeconds=60
 jcs.default.elementattributes=org.apache.commons.jcs4.engine.ElementAttributes
 jcs.default.elementattributes.IsEternal=false
 jcs.default.elementattributes.MaxLife=700
-jcs.default.elementattributes.IdleTime=1800
+jcs.default.elementattributes.MaxIdleTime=1800
 jcs.default.elementattributes.IsSpool=true
 jcs.default.elementattributes.IsRemote=true
 jcs.default.elementattributes.IsLateral=true
diff --git a/commons-jcs4-core/src/test/test-conf/TestJDBCDiskCacheRemoval.ccf 
b/commons-jcs4-core/src/test/test-conf/TestJDBCDiskCacheRemoval.ccf
index 61a86e30..44709ac8 100644
--- a/commons-jcs4-core/src/test/test-conf/TestJDBCDiskCacheRemoval.ccf
+++ b/commons-jcs4-core/src/test/test-conf/TestJDBCDiskCacheRemoval.ccf
@@ -27,7 +27,7 @@ jcs.default.cacheattributes.ShrinkerIntervalSeconds=60
 jcs.default.elementattributes=org.apache.commons.jcs4.engine.ElementAttributes
 jcs.default.elementattributes.IsEternal=false
 jcs.default.elementattributes.MaxLife=700
-jcs.default.elementattributes.IdleTime=1800
+jcs.default.elementattributes.MaxIdleTime=1800
 jcs.default.elementattributes.IsSpool=true
 jcs.default.elementattributes.IsRemote=true
 jcs.default.elementattributes.IsLateral=true
diff --git 
a/commons-jcs4-core/src/test/test-conf/TestJDBCDiskCacheSharedPool.ccf 
b/commons-jcs4-core/src/test/test-conf/TestJDBCDiskCacheSharedPool.ccf
index 8b506c2d..c5b633e1 100644
--- a/commons-jcs4-core/src/test/test-conf/TestJDBCDiskCacheSharedPool.ccf
+++ b/commons-jcs4-core/src/test/test-conf/TestJDBCDiskCacheSharedPool.ccf
@@ -27,7 +27,7 @@ jcs.default.cacheattributes.ShrinkerIntervalSeconds=60
 jcs.default.elementattributes=org.apache.commons.jcs4.engine.ElementAttributes
 jcs.default.elementattributes.IsEternal=false
 jcs.default.elementattributes.MaxLife=700
-jcs.default.elementattributes.IdleTime=1800
+jcs.default.elementattributes.MaxIdleTime=1800
 jcs.default.elementattributes.IsSpool=true
 jcs.default.elementattributes.IsRemote=true
 jcs.default.elementattributes.IsLateral=true
diff --git a/commons-jcs4-core/src/test/test-conf/TestJDBCDiskCacheShrink.ccf 
b/commons-jcs4-core/src/test/test-conf/TestJDBCDiskCacheShrink.ccf
index 4e8b15b8..9dcaf7e3 100644
--- a/commons-jcs4-core/src/test/test-conf/TestJDBCDiskCacheShrink.ccf
+++ b/commons-jcs4-core/src/test/test-conf/TestJDBCDiskCacheShrink.ccf
@@ -27,7 +27,7 @@ jcs.default.cacheattributes.ShrinkerIntervalSeconds=60
 jcs.default.elementattributes=org.apache.commons.jcs4.engine.ElementAttributes
 jcs.default.elementattributes.IsEternal=false
 jcs.default.elementattributes.MaxLife=700
-jcs.default.elementattributes.IdleTime=1800
+jcs.default.elementattributes.MaxIdleTime=1800
 jcs.default.elementattributes.IsSpool=true
 jcs.default.elementattributes.IsRemote=true
 jcs.default.elementattributes.IsLateral=true
diff --git a/commons-jcs4-core/src/test/test-conf/TestMRUCache.ccf 
b/commons-jcs4-core/src/test/test-conf/TestMRUCache.ccf
index b6bb8271..617af6fc 100644
--- a/commons-jcs4-core/src/test/test-conf/TestMRUCache.ccf
+++ b/commons-jcs4-core/src/test/test-conf/TestMRUCache.ccf
@@ -27,7 +27,7 @@ jcs.default.cacheattributes.ShrinkerIntervalSeconds=1
 jcs.default.elementattributes=org.apache.commons.jcs4.engine.ElementAttributes
 jcs.default.elementattributes.IsEternal=false
 jcs.default.elementattributes.MaxLife=600
-jcs.default.elementattributes.IdleTime=1800
+jcs.default.elementattributes.MaxIdleTime=1800
 jcs.default.elementattributes.IsSpool=true
 jcs.default.elementattributes.IsRemote=true
 jcs.default.elementattributes.IsLateral=true
diff --git a/commons-jcs4-core/src/test/test-conf/TestMySQLDiskCache.ccf 
b/commons-jcs4-core/src/test/test-conf/TestMySQLDiskCache.ccf
index 027b6c77..a236f0ba 100644
--- a/commons-jcs4-core/src/test/test-conf/TestMySQLDiskCache.ccf
+++ b/commons-jcs4-core/src/test/test-conf/TestMySQLDiskCache.ccf
@@ -29,7 +29,7 @@ jcs.default.cacheattributes.ShrinkerIntervalSeconds=60
 jcs.default.elementattributes=org.apache.commons.jcs4.engine.ElementAttributes
 jcs.default.elementattributes.IsEternal=false
 jcs.default.elementattributes.MaxLife=700
-jcs.default.elementattributes.IdleTime=1800
+jcs.default.elementattributes.MaxIdleTime=1800
 jcs.default.elementattributes.IsSpool=true
 jcs.default.elementattributes.IsRemote=true
 jcs.default.elementattributes.IsLateral=true
diff --git a/commons-jcs4-core/src/test/test-conf/TestRemoteClient.ccf 
b/commons-jcs4-core/src/test/test-conf/TestRemoteClient.ccf
index 002e1c04..6ec3e7c6 100644
--- a/commons-jcs4-core/src/test/test-conf/TestRemoteClient.ccf
+++ b/commons-jcs4-core/src/test/test-conf/TestRemoteClient.ccf
@@ -27,7 +27,7 @@ jcs.default.cacheattributes.ShrinkerIntervalSeconds=60
 jcs.default.elementattributes=org.apache.commons.jcs4.engine.ElementAttributes
 jcs.default.elementattributes.IsEternal=false
 jcs.default.elementattributes.MaxLife=700
-jcs.default.elementattributes.IdleTime=1800
+jcs.default.elementattributes.MaxIdleTime=1800
 jcs.default.elementattributes.IsSpool=true
 jcs.default.elementattributes.IsRemote=true
 jcs.default.elementattributes.IsLateral=true
diff --git a/commons-jcs4-core/src/test/test-conf/TestRemoteHttpCache.ccf 
b/commons-jcs4-core/src/test/test-conf/TestRemoteHttpCache.ccf
index 496c4391..c14de962 100644
--- a/commons-jcs4-core/src/test/test-conf/TestRemoteHttpCache.ccf
+++ b/commons-jcs4-core/src/test/test-conf/TestRemoteHttpCache.ccf
@@ -27,7 +27,7 @@ jcs.default.cacheattributes.ShrinkerIntervalSeconds=60
 jcs.default.elementattributes=org.apache.commons.jcs4.engine.ElementAttributes
 jcs.default.elementattributes.IsEternal=false
 jcs.default.elementattributes.MaxLife=700
-jcs.default.elementattributes.IdleTime=1800
+jcs.default.elementattributes.MaxIdleTime=1800
 jcs.default.elementattributes.IsSpool=true
 jcs.default.elementattributes.IsRemote=true
 jcs.default.elementattributes.IsLateral=true
diff --git a/commons-jcs4-core/src/test/test-conf/TestRemoteServer.ccf 
b/commons-jcs4-core/src/test/test-conf/TestRemoteServer.ccf
index ad2a3785..22b8db6b 100644
--- a/commons-jcs4-core/src/test/test-conf/TestRemoteServer.ccf
+++ b/commons-jcs4-core/src/test/test-conf/TestRemoteServer.ccf
@@ -38,7 +38,7 @@ jcs.default.cacheattributes.ShrinkerIntervalSeconds=60
 jcs.default.elementattributes=org.apache.commons.jcs4.engine.ElementAttributes
 jcs.default.elementattributes.IsEternal=false
 jcs.default.elementattributes.MaxLife=7000
-jcs.default.elementattributes.IdleTime=1800
+jcs.default.elementattributes.MaxIdleTime=1800
 jcs.default.elementattributes.IsSpool=true
 jcs.default.elementattributes.IsRemote=true
 jcs.default.elementattributes.IsLateral=true
diff --git a/commons-jcs4-core/src/test/test-conf/TestSimpleEventHandling.ccf 
b/commons-jcs4-core/src/test/test-conf/TestSimpleEventHandling.ccf
index b8eef784..5f2aa90e 100644
--- a/commons-jcs4-core/src/test/test-conf/TestSimpleEventHandling.ccf
+++ b/commons-jcs4-core/src/test/test-conf/TestSimpleEventHandling.ccf
@@ -55,7 +55,7 @@ jcs.region.Idletime.cacheattributes.MaxObjects=200
 
jcs.region.Idletime.cacheattributes.MemoryCacheName=org.apache.commons.jcs4.engine.memory.lru.LRUMemoryCache
 jcs.region.Idletime.elementattributes.IsEternal=false
 jcs.region.Idletime.elementattributes.MaxLife=300
-jcs.region.Idletime.elementattributes.IdleTime=1
+jcs.region.Idletime.elementattributes.MaxIdleTime=1
 
 # #### AUXILIARY CACHES
 # Indexed Disk Cache
diff --git a/commons-jcs4-core/src/test/test-conf/TestSoftReferenceCache.ccf 
b/commons-jcs4-core/src/test/test-conf/TestSoftReferenceCache.ccf
index 2d7995e3..7b4f1d85 100644
--- a/commons-jcs4-core/src/test/test-conf/TestSoftReferenceCache.ccf
+++ b/commons-jcs4-core/src/test/test-conf/TestSoftReferenceCache.ccf
@@ -25,7 +25,7 @@ jcs.default.cacheattributes.MaxMemoryIdleTimeSeconds=3600
 jcs.default.elementattributes=org.apache.commons.jcs4.engine.ElementAttributes
 jcs.default.elementattributes.IsEternal=false
 jcs.default.elementattributes.MaxLife=600
-jcs.default.elementattributes.IdleTime=1800
+jcs.default.elementattributes.MaxIdleTime=1800
 jcs.default.elementattributes.IsSpool=true
 jcs.default.elementattributes.IsRemote=true
 jcs.default.elementattributes.IsLateral=true
diff --git a/commons-jcs4-core/src/test/test-conf/TestZeroSizeCache.ccf 
b/commons-jcs4-core/src/test/test-conf/TestZeroSizeCache.ccf
index 92c724cb..44b9dd86 100644
--- a/commons-jcs4-core/src/test/test-conf/TestZeroSizeCache.ccf
+++ b/commons-jcs4-core/src/test/test-conf/TestZeroSizeCache.ccf
@@ -27,7 +27,7 @@ jcs.default.cacheattributes.ShrinkerIntervalSeconds=1
 jcs.default.elementattributes=org.apache.commons.jcs4.engine.ElementAttributes
 jcs.default.elementattributes.IsEternal=false
 jcs.default.elementattributes.MaxLife=600
-jcs.default.elementattributes.IdleTime=1800
+jcs.default.elementattributes.MaxIdleTime=1800
 jcs.default.elementattributes.IsSpool=true
 jcs.default.elementattributes.IsRemote=true
 jcs.default.elementattributes.IsLateral=true
diff --git 
a/commons-jcs4-jcache/src/main/java/org/apache/commons/jcs4/jcache/JCSCache.java
 
b/commons-jcs4-jcache/src/main/java/org/apache/commons/jcs4/jcache/JCSCache.java
index 5a692b90..e8327ffe 100644
--- 
a/commons-jcs4-jcache/src/main/java/org/apache/commons/jcs4/jcache/JCSCache.java
+++ 
b/commons-jcs4-jcache/src/main/java/org/apache/commons/jcs4/jcache/JCSCache.java
@@ -323,7 +323,7 @@ public class JCSCache<K, V> implements Cache<K, V>
             {
                 forceExpires(key);
             }
-            else if (expiryForAccess != null && 
(!elt.getElementAttributes().getIsEternal() || !expiryForAccess.isEternal()))
+            else if (expiryForAccess != null && 
(!elt.getElementAttributes().isEternal() || !expiryForAccess.isEternal()))
             {
                 try
                 {
@@ -388,11 +388,7 @@ public class JCSCache<K, V> implements Cache<K, V>
             final Duration duration = update ? 
expiryPolicy.getExpiryForUpdate() : expiryPolicy.getExpiryForCreation();
             if (isNotZero(duration))
             {
-                final IElementAttributes clone = 
delegate.getElementAttributes().clone();
-                if (ElementAttributes.class.isInstance(clone))
-                {
-                    ElementAttributes.class.cast(clone).setCreateTime();
-                }
+                final IElementAttributes clone = new 
ElementAttributes(delegate.getElementAttributes());
                 final ICacheElement<K, V> element = updateElement(key, v, 
duration, clone);
                 try
                 {
@@ -703,27 +699,31 @@ public class JCSCache<K, V> implements Cache<K, V>
             final K jcsKey = storeByValue ? copy(serializer, 
manager.getClassLoader(), key) : key;
             final ICacheElement<K, V> element = updateElement( // reuse it to 
create basic structure
                     jcsKey, value, created ? null : duration,
-                    oldElt != null ? oldElt.getElementAttributes() : 
delegate.getElementAttributes().clone());
+                    oldElt != null ? oldElt.getElementAttributes() : new 
ElementAttributes(delegate.getElementAttributes()));
             if (created && duration != null) { // set maxLife
-                final IElementAttributes copy = element.getElementAttributes();
-                copy.setTimeFactorForMilliseconds(1);
+                final long timeFactorForMilliseconds = 1;
                 final boolean eternal = duration.isEternal();
-                copy.setIsEternal(eternal);
-                if (ElementAttributes.class.isInstance(copy)) {
-                    ElementAttributes.class.cast(copy).setCreateTime();
-                }
+                long maxIdleTime = 
element.getElementAttributes().maxIdleTime();
+                long maxLife = element.getElementAttributes().maxLife();
                 if (!eternal)
                 {
-                    copy.setIsEternal(false);
                     if (duration == expiryPolicy.getExpiryForAccess())
                     {
-                        
element.getElementAttributes().setIdleTime(duration.getTimeUnit().toMillis(duration.getDurationAmount()));
+                        maxIdleTime = 
duration.getTimeUnit().toMillis(duration.getDurationAmount());
                     }
                     else
                     {
-                        
element.getElementAttributes().setMaxLife(duration.getTimeUnit().toMillis(duration.getDurationAmount()));
+                        maxLife = 
duration.getTimeUnit().toMillis(duration.getDurationAmount());
                     }
                 }
+                IElementAttributes copy = new ElementAttributes(
+                        element.getElementAttributes().isSpool(),
+                        element.getElementAttributes().isLateral(),
+                        element.getElementAttributes().isRemote(),
+                        eternal,
+                        maxLife,
+                        maxIdleTime,
+                        timeFactorForMilliseconds);
                 element.setElementAttributes(copy);
             }
             writer.write(new JCSEntry<>(jcsKey, value));
@@ -917,7 +917,7 @@ public class JCSCache<K, V> implements Cache<K, V>
             if (value != null)
             {
                 final Duration expiryForAccess = 
expiryPolicy.getExpiryForAccess();
-                if (expiryForAccess != null && 
(!elt.getElementAttributes().getIsEternal() || !expiryForAccess.isEternal()))
+                if (expiryForAccess != null && 
(!elt.getElementAttributes().isEternal() || !expiryForAccess.isEternal()))
                 {
                     try
                     {
@@ -970,19 +970,21 @@ public class JCSCache<K, V> implements Cache<K, V>
 
     private ICacheElement<K, V> updateElement(final K key, final V v, final 
Duration duration, final IElementAttributes attrs)
     {
-        final ICacheElement<K, V> element = new CacheElement<>(name, key, v);
+        final ICacheElement<K, V> element = new CacheElement<>(name, key, v, 
attrs);
         if (duration != null)
         {
-            attrs.setTimeFactorForMilliseconds(1);
             final boolean eternal = duration.isEternal();
-            attrs.setIsEternal(eternal);
-            if (!eternal)
-            {
-                attrs.setLastAccessTimeNow();
-            }
             // MaxLife = -1 to use IdleTime excepted if jcache.ccf asked for 
something else
+            IElementAttributes copy = new ElementAttributes(
+                    attrs.isSpool(),
+                    attrs.isLateral(),
+                    attrs.isRemote(),
+                    eternal,
+                    attrs.maxLife(),
+                    attrs.maxIdleTime(),
+                    1);
+            element.setElementAttributes(copy);
         }
-        element.setElementAttributes(attrs);
         return element;
     }
 }
diff --git 
a/commons-jcs4-jcache/src/main/java/org/apache/commons/jcs4/jcache/JCSCachingManager.java
 
b/commons-jcs4-jcache/src/main/java/org/apache/commons/jcs4/jcache/JCSCachingManager.java
index 023afb6a..6980a43a 100644
--- 
a/commons-jcs4-jcache/src/main/java/org/apache/commons/jcs4/jcache/JCSCachingManager.java
+++ 
b/commons-jcs4-jcache/src/main/java/org/apache/commons/jcs4/jcache/JCSCachingManager.java
@@ -87,7 +87,7 @@ public class JCSCachingManager implements CacheManager
        
jcs.default.elementattributes=org.apache.commons.jcs4.engine.ElementAttributes
        jcs.default.elementattributes.IsEternal=false
        jcs.default.elementattributes.MaxLife=700
-       jcs.default.elementattributes.IdleTime=1800
+       jcs.default.elementattributes.MaxIdleTime=1800
        jcs.default.elementattributes.IsSpool=true
        jcs.default.elementattributes.IsRemote=true
        jcs.default.elementattributes.IsLateral=true
diff --git a/xdocs/ElementAttributes.xml b/xdocs/ElementAttributes.xml
index 3d101d7b..8332c53f 100644
--- a/xdocs/ElementAttributes.xml
+++ b/xdocs/ElementAttributes.xml
@@ -59,7 +59,7 @@ jcs.default.cacheattributes.ShrinkerIntervalSeconds=60
 jcs.default.elementattributes=org.apache.commons.jcs4.engine.ElementAttributes
 jcs.default.elementattributes.IsEternal=false
 jcs.default.elementattributes.MaxLife=700
-jcs.default.elementattributes.IdleTime=1800
+jcs.default.elementattributes.MaxIdleTime=1800
 jcs.default.elementattributes.IsSpool=true
 jcs.default.elementattributes.IsRemote=true
 jcs.default.elementattributes.IsLateral=true
diff --git a/xdocs/IndexedDiskAuxCache.xml b/xdocs/IndexedDiskAuxCache.xml
index 7fd61f41..75ebfa17 100644
--- a/xdocs/IndexedDiskAuxCache.xml
+++ b/xdocs/IndexedDiskAuxCache.xml
@@ -258,7 +258,7 @@ jcs.default.cacheattributes.ShrinkerIntervalSeconds=60
 jcs.default.elementattributes=org.apache.commons.jcs4.engine.ElementAttributes
 jcs.default.elementattributes.IsEternal=false
 jcs.default.elementattributes.MaxLife=700
-jcs.default.elementattributes.IdleTime=1800
+jcs.default.elementattributes.MaxIdleTime=1800
 jcs.default.elementattributes.IsSpool=true
 jcs.default.elementattributes.IsRemote=true
 jcs.default.elementattributes.IsLateral=true
diff --git a/xdocs/JDBCDiskCache.xml b/xdocs/JDBCDiskCache.xml
index 2c0ed021..bb972a28 100644
--- a/xdocs/JDBCDiskCache.xml
+++ b/xdocs/JDBCDiskCache.xml
@@ -47,7 +47,7 @@ jcs.default.cacheattributes.ShrinkerIntervalSeconds=60
 jcs.default.elementattributes=org.apache.commons.jcs4.engine.ElementAttributes
 jcs.default.elementattributes.IsEternal=false
 jcs.default.elementattributes.MaxLife=14400
-jcs.default.elementattributes.IdleTime=14400
+jcs.default.elementattributes.MaxIdleTime=14400
 jcs.default.elementattributes.IsSpool=true
 jcs.default.elementattributes.IsRemote=true
 jcs.default.elementattributes.IsLateral=true
@@ -104,7 +104,7 @@ jcs.default.cacheattributes.ShrinkerIntervalSeconds=60
 jcs.default.elementattributes=org.apache.commons.jcs4.engine.ElementAttributes
 jcs.default.elementattributes.IsEternal=false
 jcs.default.elementattributes.MaxLife=700
-jcs.default.elementattributes.IdleTime=1800
+jcs.default.elementattributes.MaxIdleTime=1800
 jcs.default.elementattributes.IsSpool=true
 jcs.default.elementattributes.IsRemote=true
 jcs.default.elementattributes.IsLateral=true
@@ -178,7 +178,7 @@ jcs.default.cacheattributes.ShrinkerIntervalSeconds=60
 jcs.default.elementattributes=org.apache.commons.jcs4.engine.ElementAttributes
 jcs.default.elementattributes.IsEternal=false
 jcs.default.elementattributes.MaxLife=700
-jcs.default.elementattributes.IdleTime=1800
+jcs.default.elementattributes.MaxIdleTime=1800
 jcs.default.elementattributes.IsSpool=true
 jcs.default.elementattributes.IsRemote=true
 jcs.default.elementattributes.IsLateral=true
diff --git a/xdocs/UpgradingFrom3x.xml b/xdocs/UpgradingFrom3x.xml
index 61fa52bd..d18d1bf2 100644
--- a/xdocs/UpgradingFrom3x.xml
+++ b/xdocs/UpgradingFrom3x.xml
@@ -115,15 +115,20 @@ 
jcs.default.cacheattributes=org.apache.commons.jcs4.engine.CompositeCacheAttribu
                    <th>New Property Name</th>
                </tr>
                <tr>
-                   <td>Region configuration (CompositeCacheAttributes)</td>
+                   <td>Region configuration (cacheattributes)</td>
                    <td>DiskUsagePatternName</td>
                    <td>DiskUsagePattern</td>
                </tr>
+               <tr>
+                   <td>Element configuration (elementattributes)</td>
+                   <td>IdleTime</td>
+                   <td>MaxIdleTime</td>
+               </tr>
             </table>
          </p>
          <p>
-           The documentation in the code always stated that region 
configurations inherit 
-           values from the default region settings. This, however, was never 
implemented.
+           The documentation in the code always stated that region and element 
configurations 
+           inherit values from the default region attributes. This, however, 
was never implemented.
            Note that the behavior has been fixed in JCS 4.0.0. This may have 
implications
            for your existing configurations. On the other hand, it makes the 
configuration
            much shorter.
diff --git a/xdocs/UsingJCSBasicWeb.xml b/xdocs/UsingJCSBasicWeb.xml
index 14eaee91..c44507de 100644
--- a/xdocs/UsingJCSBasicWeb.xml
+++ b/xdocs/UsingJCSBasicWeb.xml
@@ -369,7 +369,7 @@ jcs.default.cacheattributes.MemoryCacheName=
     org.apache.commons.jcs4.engine.memory.lru.LRUMemoryCache
 jcs.default.elementattributes.IsEternal=false
 jcs.default.elementattributes.MaxLife=3600
-jcs.default.elementattributes.IdleTime=1800
+jcs.default.elementattributes.MaxIdleTime=1800
 jcs.default.elementattributes.IsSpool=true
 jcs.default.elementattributes.IsRemote=true
 jcs.default.elementattributes.IsLateral=true
@@ -386,7 +386,7 @@ jcs.region.bookCache.cacheattributes.MemoryCacheName=
     org.apache.commons.jcs4.engine.memory.lru.LRUMemoryCache
 jcs.region.bookCache.elementattributes.IsEternal=false
 jcs.region.bookCache.elementattributes.MaxLife=7200
-jcs.region.bookCache.elementattributes.IdleTime=1800
+jcs.region.bookCache.elementattributes.MaxIdleTime=1800
 jcs.region.bookCache.elementattributes.IsSpool=true
 jcs.region.bookCache.elementattributes.IsRemote=true
 jcs.region.bookCache.elementattributes.IsLateral=true
diff --git a/xdocs/getting_started/intro.xml b/xdocs/getting_started/intro.xml
index 9b7ebacc..70ad69ab 100644
--- a/xdocs/getting_started/intro.xml
+++ b/xdocs/getting_started/intro.xml
@@ -157,7 +157,7 @@ jcs.default.cacheattributes.ShrinkerIntervalSeconds=60
 jcs.default.elementattributes=org.apache.commons.jcs4.engine.ElementAttributes
 jcs.default.elementattributes.IsEternal=false
 jcs.default.elementattributes.MaxLife=21600
-jcs.default.elementattributes.IdleTime=1800
+jcs.default.elementattributes.MaxIdleTime=1800
 jcs.default.elementattributes.IsSpool=true
 jcs.default.elementattributes.IsRemote=true
 jcs.default.elementattributes.IsLateral=true

Reply via email to