This breaks the build. Why not let Moditect handle the JPMS noise?
Gary On Fri, Jan 30, 2026, 11:17 <[email protected]> wrote: > 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 > > > The following commit(s) were added to refs/heads/master by this push: > new 29d49bf4 Add optional JSON serializer/deserializer based on > Jackson > 29d49bf4 is described below > > commit 29d49bf48e6d8937e7c1c0677886965dd79e10fb > Author: Thomas Vandahl <[email protected]> > AuthorDate: Fri Jan 30 17:16:54 2026 +0100 > > Add optional JSON serializer/deserializer based on Jackson > --- > commons-jcs3-core/pom.xml | 7 + > commons-jcs3-core/src/main/java/module-info.java | 13 +- > .../jcs3/utils/serialization/JSONSerializer.java | 99 ++++++++++++++ > .../serialization/JSONSerializerUnitTest.java | 143 > +++++++++++++++++++++ > .../utils/serialization/SerializerUnitTest.java | 90 ++++++++----- > .../src/test/test-conf/TestElementSerializer.ccf | 12 ++ > src/changes/changes.xml | 3 + > 7 files changed, 332 insertions(+), 35 deletions(-) > > diff --git a/commons-jcs3-core/pom.xml b/commons-jcs3-core/pom.xml > index c0376525..9dac65c8 100644 > --- a/commons-jcs3-core/pom.xml > +++ b/commons-jcs3-core/pom.xml > @@ -103,6 +103,13 @@ > <artifactId>jakarta.servlet-api</artifactId> > <optional>true</optional> > </dependency> > + > + <dependency> > + <groupId>com.fasterxml.jackson.core</groupId> > + <artifactId>jackson-databind</artifactId> > + <version>2.20.1</version> > + <optional>true</optional> > + </dependency> > </dependencies> > > <build> > diff --git a/commons-jcs3-core/src/main/java/module-info.java > b/commons-jcs3-core/src/main/java/module-info.java > index aed49d22..5d9a95b1 100644 > --- a/commons-jcs3-core/src/main/java/module-info.java > +++ b/commons-jcs3-core/src/main/java/module-info.java > @@ -93,14 +93,14 @@ module org.apache.commons.jcs3.core { > > // Java platform modules - required > requires java.base; > - requires java.rmi; > - requires java.sql; > requires java.management; > - requires java.naming; > requires java.desktop; > + requires transitive java.rmi; > + requires transitive java.sql; > + requires transitive java.naming; > > // Optional dependencies for remote HTTP caching > - requires static jakarta.servlet; > + requires transitive jakarta.servlet; > > // Optional dependencies for JDBC disk cache > requires static org.apache.commons.dbcp2; > @@ -109,6 +109,11 @@ module org.apache.commons.jcs3.core { > requires static org.apache.httpcomponents.httpclient; > requires static org.apache.httpcomponents.httpcore; > > + // Optional dependencies for remote HTTP caching > + requires static com.fasterxml.jackson.databind; > + opens org.apache.commons.jcs3.utils.serialization to > com.fasterxml.jackson.databind; > + opens org.apache.commons.jcs3.engine to > com.fasterxml.jackson.databind; > + > // Uses and provides clauses > uses org.apache.commons.jcs3.auxiliary.AuxiliaryCacheFactory; > } > diff --git > a/commons-jcs3-core/src/main/java/org/apache/commons/jcs3/utils/serialization/JSONSerializer.java > b/commons-jcs3-core/src/main/java/org/apache/commons/jcs3/utils/serialization/JSONSerializer.java > new file mode 100644 > index 00000000..b563d060 > --- /dev/null > +++ > b/commons-jcs3-core/src/main/java/org/apache/commons/jcs3/utils/serialization/JSONSerializer.java > @@ -0,0 +1,99 @@ > +package org.apache.commons.jcs3.utils.serialization; > + > +/* > + * 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 > + * > + * http://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. > + */ > + > +import java.io.IOException; > + > +import org.apache.commons.jcs3.engine.behavior.IElementSerializer; > + > +import com.fasterxml.jackson.core.exc.StreamReadException; > +import com.fasterxml.jackson.databind.DatabindException; > +import com.fasterxml.jackson.databind.JsonNode; > +import com.fasterxml.jackson.databind.ObjectMapper; > +import com.fasterxml.jackson.databind.json.JsonMapper; > + > +/** > + * Performs JSON serialization and de-serialization. > + */ > +public class JSONSerializer > + implements IElementSerializer > +{ > + /** Jackson JSON mapper instance */ > + private static final ObjectMapper mapper = > JsonMapper.builder().build(); > + > + /** Wrapper to save the class name information */ > + private record Wrapper<T>(String className, T element) {} > + > + /** > + * Uses JSON de-serialization to turn a byte array into an object. > All exceptions are > + * converted into IOExceptions. > + * > + * @param data data bytes > + * @param loader class loader to use > + * @return Object > + * @throws IOException > + * @throws ClassNotFoundException > + */ > + @Override > + public <T> T deSerialize(final byte[] data, final ClassLoader loader) > + throws IOException, ClassNotFoundException > + { > + if (data == null || data.length == 0) > + { > + return null; > + } > + > + try > + { > + JsonNode root = mapper.readTree(data); > + String className = root.at("/className").asText(); > + > + @SuppressWarnings("unchecked") // Need to cast from Object > + Class<T> clazz = (Class<T>)Class.forName(className, false, > + loader == null ? this.getClass().getClassLoader() : > loader); > + > + return mapper.treeToValue(root.at("/element"), clazz); > + } > + catch (StreamReadException | DatabindException e) > + { > + throw new IOException("Error deserializing JSON", e); > + } > + } > + > + /** > + * Serializes an object using JSON serialization. > + * > + * @param obj > + * @return byte[] > + * @throws IOException > + */ > + @Override > + public <T> byte[] serialize(final T obj) > + throws IOException > + { > + if (obj == null) > + { > + return null; > + } > + > + Wrapper<T> wrapper = new Wrapper<T>(obj.getClass().getName(), > obj); > + return mapper.writeValueAsBytes(wrapper); > + } > +} > diff --git > a/commons-jcs3-core/src/test/java/org/apache/commons/jcs3/utils/serialization/JSONSerializerUnitTest.java > b/commons-jcs3-core/src/test/java/org/apache/commons/jcs3/utils/serialization/JSONSerializerUnitTest.java > new file mode 100644 > index 00000000..c786598a > --- /dev/null > +++ > b/commons-jcs3-core/src/test/java/org/apache/commons/jcs3/utils/serialization/JSONSerializerUnitTest.java > @@ -0,0 +1,143 @@ > +package org.apache.commons.jcs3.utils.serialization; > + > +/* > + * 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. > + */ > + > +import static org.junit.jupiter.api.Assertions.assertEquals; > +import static org.junit.jupiter.api.Assertions.assertNull; > + > +import java.nio.charset.StandardCharsets; > +import java.util.Calendar; > +import java.util.Date; > +import java.util.GregorianCalendar; > + > +import org.junit.jupiter.api.BeforeEach; > +import org.junit.jupiter.api.Test; > + > +/** > + * Tests the JSON serializer. > + */ > +class JSONSerializerUnitTest > +{ > + private JSONSerializer serializer; > + > + /** > + * Test setup > + * > + * @throws Exception > + */ > + @BeforeEach > + void setUp() > + throws Exception > + { > + this.serializer = new JSONSerializer(); > + } > + > + public static record Person(String name, int age, Date birthdate) {} > + > + /** > + * Test simple back and forth with an object. > + * > + * @throws Exception > + */ > + @Test > + void testObjectBackAndForth() > + throws Exception > + { > + Date date = new GregorianCalendar(1977, Calendar.DECEMBER, > 15).getTime(); > + final Person before = new Person("joe", 21, date); > + > + // DO WORK > + byte[] serialized = serializer.serialize(before); > + System.out.println(new String(serialized, > StandardCharsets.UTF_8)); > + final Person after = serializer.deSerialize(serialized, null); > + > + // VERIFY > + assertEquals(before.name(), after.name(), "Before and after > should be the same."); > + assertEquals(before.age(), after.age(), "Before and after should > be the same."); > + assertEquals(before.birthdate(), after.birthdate(), "Before and > after should be the same."); > + } > + > + /** > + * Test simple back and forth with a string. > + * > + * @throws Exception > + */ > + @Test > + void testBigStringBackAndForth() > + throws Exception > + { > + final String string = "This is my big string ABCDEFGH"; > + final StringBuilder sb = new StringBuilder(); > + sb.append( string ); > + for ( int i = 0; i < 4; i++ ) > + { > + sb.append( " " + i + sb.toString() ); // big string > + } > + final String before = sb.toString(); > + > + // DO WORK > + final String after = (String) serializer.deSerialize( > serializer.serialize( before ), null ); > + > + // VERIFY > + assertEquals( before, after, "Before and after should be the > same." ); > + } > + > + /** > + * Test serialization with a null object. Verify that we don't get an > error. > + * > + * @throws Exception > + */ > + @Test > + void testNullInput() > + throws Exception > + { > + final String before = null; > + > + // DO WORK > + final byte[] serialized = serializer.serialize( before ); > + //System.out.println( "testNullInput " + serialized ); > + > + final String after = serializer.deSerialize( serialized, null ); > + //System.out.println( "testNullInput " + after ); > + > + // VERIFY > + assertNull( after, "Should have nothing." ); > + } > + > + /** > + * Test simple back and forth with a string. > + * > + * @throws Exception > + */ > + @Test > + void testSimpleBackAndForth() > + throws Exception > + { > + final String before = > "adsfdsafdsafdsafdsafdsafdsafdsagfdsafdsafdsfdsafdsafsa333 31231"; > + > + // DO WORK > + byte[] serialized = serializer.serialize(before); > + System.out.println(new String(serialized, > StandardCharsets.UTF_8)); > + final String after = serializer.deSerialize(serialized, null); > + > + // VERIFY > + assertEquals( before, after, "Before and after should be the > same." ); > + } > +} > diff --git > a/commons-jcs3-core/src/test/java/org/apache/commons/jcs3/utils/serialization/SerializerUnitTest.java > b/commons-jcs3-core/src/test/java/org/apache/commons/jcs3/utils/serialization/SerializerUnitTest.java > index a16e25b3..a3e87d80 100644 > --- > a/commons-jcs3-core/src/test/java/org/apache/commons/jcs3/utils/serialization/SerializerUnitTest.java > +++ > b/commons-jcs3-core/src/test/java/org/apache/commons/jcs3/utils/serialization/SerializerUnitTest.java > @@ -23,8 +23,8 @@ import static > org.junit.jupiter.api.Assertions.assertNotNull; > > import org.apache.commons.jcs3.JCS; > import org.apache.commons.jcs3.access.CacheAccess; > -import org.junit.jupiter.api.AfterEach; > -import org.junit.jupiter.api.BeforeEach; > +import org.junit.jupiter.api.AfterAll; > +import org.junit.jupiter.api.BeforeAll; > import org.junit.jupiter.api.Test; > > /** > @@ -37,68 +37,96 @@ class SerializerUnitTest > * > * @throws Exception > */ > - @BeforeEach > - void setUp() > + @BeforeAll > + public static void setUp() > throws Exception > { > JCS.setConfigFilename( "/TestElementSerializer.ccf" ); > } > > - @AfterEach > - void tearDown() > + @AfterAll > + public static void tearDown() > throws Exception > { > JCS.shutdown(); > } > > /** > - * Verify that object reading and writing works > + * Verify that object reading and writing with CompressingSerializer > works > * > * @throws Exception > */ > @Test > - void testReadWrite() > + public void testReadWriteCompressingSerializer() > throws Exception > { > - final int count = 500; // 100 fit in memory > // CompressingSerializer > - final CacheAccess<String, String> jcs1 = JCS.getInstance( > "blockRegion1" ); > + final CacheAccess<String, String> jcs = JCS.getInstance( > "blockRegion1" ); > > - for ( int i = 0; i < count; i++ ) > - { > - jcs1.put( "key:" + i, "data" + i ); > - } > + testReadWrite(jcs); > + } > > - for ( int i = 0; i < count; i++ ) > - { > - final String res = jcs1.get( "key:" + i ); > - assertNotNull( res, "[key:" + i + "] should not be null, " + > jcs1.getStats() ); > - } > + /** > + * Verify that object reading and writing with EncryptingSerializer > works > + * > + * @throws Exception > + */ > + @Test > + public void testReadWriteEncryptingSerializer() > + throws Exception > + { > + // EncryptingSerializer > + final CacheAccess<String, String> jcs1 = JCS.getInstance( > "blockRegion2" ); > + > + testReadWrite(jcs1); > + > + JCS.shutdown(); > > + // Re-init > // EncryptingSerializer > final CacheAccess<String, String> jcs2 = JCS.getInstance( > "blockRegion2" ); > > - for ( int i = 0; i < count; i++ ) > - { > - jcs2.put( "key:" + i, "data" + i ); > - } > - > - for ( int i = 0; i < count; i++ ) > + for ( int i = 0; i < 500; i++ ) > { > final String res = jcs2.get( "key:" + i ); > assertNotNull( res, "[key:" + i + "] should not be null, " + > jcs2.getStats() ); > } > + } > > - JCS.shutdown(); > + /** > + * Verify that object reading and writing with JSONSerializer works > + * > + * @throws Exception > + */ > + @Test > + public void testReadWriteJSONSerializer() > + throws Exception > + { > + // JSONSerializer > + final CacheAccess<String, String> jcs = JCS.getInstance( > "blockRegion3" ); > > - // Re-init > - // EncryptingSerializer > - final CacheAccess<String, String> jcs3 = JCS.getInstance( > "blockRegion2" ); > + testReadWrite(jcs); > + } > + > + /** > + * Verify that object reading and writing works > + * > + * @throws Exception > + */ > + private void testReadWrite(CacheAccess<String, String> jcs) > + throws Exception > + { > + final int count = 500; // 100 fit in memory > + > + for ( int i = 0; i < count; i++ ) > + { > + jcs.put( "key:" + i, "data" + i ); > + } > > for ( int i = 0; i < count; i++ ) > { > - final String res = jcs3.get( "key:" + i ); > - assertNotNull( res, "[key:" + i + "] should not be null, " + > jcs3.getStats() ); > + final String res = jcs.get( "key:" + i ); > + assertNotNull( res, "[key:" + i + "] should not be null, " + > jcs.getStats() ); > } > } > } > diff --git > a/commons-jcs3-core/src/test/test-conf/TestElementSerializer.ccf > b/commons-jcs3-core/src/test/test-conf/TestElementSerializer.ccf > index 6831c89f..562f80a8 100644 > --- a/commons-jcs3-core/src/test/test-conf/TestElementSerializer.ccf > +++ b/commons-jcs3-core/src/test/test-conf/TestElementSerializer.ccf > @@ -41,6 +41,11 @@ > jcs.region.blockRegion2.cacheattributes=org.apache.commons.jcs3.engine.Composite > jcs.region.blockRegion2.cacheattributes.MaxObjects=100 > > > jcs.region.blockRegion2.cacheattributes.MemoryCacheName=org.apache.commons.jcs3.engine.memory.lru.LRUMemoryCache > > +jcs.region.blockRegion3=blockDiskCache3 > > +jcs.region.blockRegion3.cacheattributes=org.apache.commons.jcs3.engine.CompositeCacheAttributes > +jcs.region.blockRegion3.cacheattributes.MaxObjects=100 > > +jcs.region.blockRegion3.cacheattributes.MemoryCacheName=org.apache.commons.jcs3.engine.memory.lru.LRUMemoryCache > + > # #### AUXILIARY CACHES > > # Block Disk Cache > @@ -59,6 +64,13 @@ > jcs.auxiliary.blockDiskCache2.serializer=org.apache.commons.jcs3.utils.serializa > jcs.auxiliary.blockDiskCache2.serializer.attributes.preSharedKey=my_secret > > > jcs.auxiliary.blockDiskCache2.serializer.attributes.aesCipherTransformation=AES/GCM/NoPadding > > +# Block Disk Cache > > +jcs.auxiliary.blockDiskCache3=org.apache.commons.jcs3.auxiliary.disk.block.BlockDiskCacheFactory > > +jcs.auxiliary.blockDiskCache3.attributes=org.apache.commons.jcs3.auxiliary.disk.block.BlockDiskCacheAttributes > > +jcs.auxiliary.blockDiskCache3.attributes.DiskPath=target/test-sandbox/block-disk-cache3 > +jcs.auxiliary.blockDiskCache3.attributes.EventQueueType=POOLED > > +jcs.auxiliary.blockDiskCache3.serializer=org.apache.commons.jcs3.utils.serialization.JSONSerializer > + > # Default Cache Event Queue thread pool config, used by auxiliaries > thread_pool.cache_event_queue.useBoundary=false > #thread_pool.cache_event_queue.boundarySize=2000 > diff --git a/src/changes/changes.xml b/src/changes/changes.xml > index 4b243dc3..27c2a120 100644 > --- a/src/changes/changes.xml > +++ b/src/changes/changes.xml > @@ -37,6 +37,9 @@ > <action dev="tv" type="add"> > Add module-info.java for JCS core > </action> > + <action dev="tv" type="add"> > + Add optional JSON serializer/deserializer based on Jackson > + </action> > <!-- REMOVE --> > <action dev="tv" type="remove"> > Remove all deprecated code. > >
