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

Reply via email to