Re: (commons-jcs) branch master updated: Add optional JSON serializer/deserializer based on Jackson

Gary Gregory <[email protected]>
Newsgroups gmane.comp.jakarta.commons.devel
Message-ID <CACZkXPwVLQaxaaO_rvzq7V4vmHu19KRGTHU+mPggS40TLxN2fg@mail.gmail.com>
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.
>
>
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.