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

juanpablo pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/jspwiki.git

commit e1f7f93b3518397e2f1b050f4cb71a249e52756a
Author: Juan Pablo Santos Rodríguez <[email protected]>
AuthorDate: Thu Dec 2 16:54:01 2021 +0100

    New jspwiki-cache module
    
    * Cache backend can now be overriden by providing a custom CachingManager 
via 
[classmappings-extra.xml](https://jspwiki-wiki.apache.org/Wiki.jsp?page=JSPWikiPublicAPI#section-JSPWikiPublicAPI-RegisteringCustomManagersInTheWikiEngine)
    * Default cache manager remains ehcache-based, with default configuration 
file located at ehcache-jspwiki.xml
    * Tests wanting to invalidate cache(s) should call either 
`Engine#shutdown()` or `Engine#getManager( CachingManager.class ).shutdown()`
    * The `jspwiki.cache.config-file` setting on the 
`jspwiki[-custom].properties` file allows to use a custom ehcache configuration 
file, located elsewhere on classpath
    
    Also, Introduced `TextUtil#getStringProperty( Properties, String key, 
String deprecatedKey, String defval )` to allow deprecation of properties, so 
they can be removed later on
    
    Also, deprecated property jspwiki.usePageCache in favour of 
jspwiki.cache.enable
---
 jspwiki-cache/pom.xml                              |  95 ++++++++++++
 .../main/java/org/apache/wiki/cache/CacheInfo.java |  62 ++++++++
 .../java/org/apache/wiki/cache/CachingManager.java | 124 +++++++++++++++
 .../apache/wiki/cache/EhcacheCachingManager.java   | 166 +++++++++++++++++++++
 .../main/java/org/apache/wiki/cache/package.html   |  29 ++++
 .../src/main/resources/ehcache-jspwiki.xml         |  22 +--
 .../wiki/cache/EhcacheCachingManagerTest.java      |  85 +++++++++++
 .../src/test/resources/ehcache-jspwiki-test.xml    |  18 +--
 .../src/main/resources/ini/classmappings.xml       |   4 +
 .../src/main/resources/ini/jspwiki.properties      |   4 +-
 .../src/test/resources/ini/jspwiki.properties      |   2 +-
 .../WEB-INF/classes/ehcache-woas-department.xml}   |   0
 .../WEB-INF/classes/jspwiki-custom.properties      |   3 +-
 .../WEB-INF/classes/ehcache-woas-personal.xml}     |   0
 .../WEB-INF/classes/jspwiki-custom.properties      |   3 +-
 .../java/org/apache/wiki/util/CheckedSupplier.java |  39 +++++
 .../main/java/org/apache/wiki/util/TextUtil.java   |  60 +++++---
 .../java/org/apache/wiki/util/TextUtilTest.java    |   9 ++
 .../src/test/resources/ini/jspwiki.properties      |   2 +-
 19 files changed, 676 insertions(+), 51 deletions(-)

diff --git a/jspwiki-cache/pom.xml b/jspwiki-cache/pom.xml
new file mode 100644
index 0000000..f6a1d01
--- /dev/null
+++ b/jspwiki-cache/pom.xml
@@ -0,0 +1,95 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+  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.
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0"; 
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"; 
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 
http://maven.apache.org/maven-v4_0_0.xsd";>
+
+  <parent>
+    <groupId>org.apache.jspwiki</groupId>
+    <artifactId>jspwiki-builder</artifactId>
+    <version>2.11.1-SNAPSHOT</version>
+  </parent>
+
+  <modelVersion>4.0.0</modelVersion>
+  <artifactId>jspwiki-cache</artifactId>
+  <name>Apache JSPWiki cache abstraction</name>
+
+  <dependencies>
+    <dependency>
+      <groupId>${project.groupId}</groupId>
+      <artifactId>jspwiki-api</artifactId>
+      <version>${project.version}</version>
+    </dependency>
+
+       <dependency>
+      <groupId>${project.groupId}</groupId>
+      <artifactId>jspwiki-util</artifactId>
+      <version>${project.version}</version>
+    </dependency>
+
+    <dependency>
+      <groupId>org.apache.logging.log4j</groupId>
+      <artifactId>log4j-api</artifactId>
+    </dependency>
+
+    <dependency>
+      <groupId>org.apache.logging.log4j</groupId>
+      <artifactId>log4j-1.2-api</artifactId>
+    </dependency>
+
+    <dependency> <!-- ehcache brings slf4j, so we route it to log4j2 -->
+      <groupId>org.apache.logging.log4j</groupId>
+      <artifactId>log4j-slf4j-impl</artifactId>
+    </dependency>
+
+    <dependency>
+      <groupId>net.sf.ehcache</groupId>
+      <artifactId>ehcache</artifactId>
+    </dependency>
+
+    <dependency>
+      <groupId>org.junit.jupiter</groupId>
+      <artifactId>junit-jupiter-api</artifactId>
+      <scope>test</scope>
+    </dependency>
+
+    <dependency>
+      <groupId>org.junit.jupiter</groupId>
+      <artifactId>junit-jupiter-params</artifactId>
+      <scope>test</scope>
+    </dependency>
+
+    <dependency>
+      <groupId>org.junit.jupiter</groupId>
+      <artifactId>junit-jupiter-engine</artifactId>
+      <scope>test</scope>
+    </dependency>
+
+    <dependency>
+      <groupId>org.mockito</groupId>
+      <artifactId>mockito-core</artifactId>
+      <scope>test</scope>
+    </dependency>
+
+    <dependency>
+      <groupId>org.mockito</groupId>
+      <artifactId>mockito-junit-jupiter</artifactId>
+      <scope>test</scope>
+    </dependency>
+  </dependencies>
+</project>
diff --git a/jspwiki-cache/src/main/java/org/apache/wiki/cache/CacheInfo.java 
b/jspwiki-cache/src/main/java/org/apache/wiki/cache/CacheInfo.java
new file mode 100644
index 0000000..32ada21
--- /dev/null
+++ b/jspwiki-cache/src/main/java/org/apache/wiki/cache/CacheInfo.java
@@ -0,0 +1,62 @@
+/*
+    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.
+ */
+
+package org.apache.wiki.cache;
+
+/**
+ * Simple pojo that holds cache information.
+ */
+public class CacheInfo {
+
+    private final String name;
+    private final long maxElementsAllowed;
+    private long misses;
+    private long hits;
+
+    public CacheInfo( final String name, final long maxElementsAllowed ) {
+        this.name = name;
+        this.maxElementsAllowed = maxElementsAllowed;
+        this.hits = 0L;
+        this.misses = 0L;
+    }
+
+    public void hit() {
+        hits++;
+    }
+
+    public void miss() {
+        misses++;
+    }
+
+    public String getName() {
+        return name;
+    }
+
+    public long getMisses() {
+        return misses;
+    }
+
+    public long getHits() {
+        return hits;
+    }
+
+    public long getMaxElementsAllowed() {
+        return maxElementsAllowed;
+    }
+}
diff --git 
a/jspwiki-cache/src/main/java/org/apache/wiki/cache/CachingManager.java 
b/jspwiki-cache/src/main/java/org/apache/wiki/cache/CachingManager.java
new file mode 100644
index 0000000..245e1ef
--- /dev/null
+++ b/jspwiki-cache/src/main/java/org/apache/wiki/cache/CachingManager.java
@@ -0,0 +1,124 @@
+/*
+    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.
+ */
+package org.apache.wiki.cache;
+
+
+import org.apache.wiki.util.CheckedSupplier;
+
+import java.io.Serializable;
+import java.util.List;
+
+/**
+ * Cache manager abstraction.
+ */
+public interface CachingManager {
+
+    /**
+     * The property value for setting the cache on/off.  Value is {@value}.
+     * This key is deprecated, use instead {@link #PROP_CACHE_ENABLE}.
+     */
+    @Deprecated
+    String PROP_USECACHE_DEPRECATED = "jspwiki.usePageCache";
+
+    /** The property value for setting the cache on/off.  Value is {@value}. */
+    String PROP_CACHE_ENABLE = "jspwiki.cache.enable";
+
+    /** The property value with the location of the cache configuration file.  
Value is {@value}. */
+    String PROP_CACHE_CONF_FILE = "jspwiki.cache.config-file";
+
+    /** Name of the attachment cache. */
+    String CACHE_ATTACHMENTS = "jspwiki.attachmentsCache";
+
+    /** Name of the collection attachment cache. */
+    String CACHE_ATTACHMENTS_COLLECTION = "jspwiki.attachmentCollectionsCache";
+
+    /** Name of the dynamic attachment cache. */
+    String CACHE_ATTACHMENTS_DYNAMIC = "jspwiki.dynamicAttachmentCache";
+
+    /** Name of the page cache. */
+    String CACHE_PAGES = "jspwiki.pageCache";
+
+    /** Name of the page text cache. */
+    String CACHE_PAGES_TEXT = "jspwiki.pageTextCache";
+
+    /** Name of the page history cache. */
+    String CACHE_PAGES_HISTORY = "jspwiki.pageHistoryCache";
+
+    /** Name of the rendering cache. */
+    String CACHE_DOCUMENTS = "jspwiki.renderingCache";
+
+    /**
+     * Shuts down the underlying cache manager
+     */
+    void shutdown();
+
+    /**
+     * Checks if the requested cache is enabled or not.
+     *
+     * @param cacheName The cache to be queried.
+     * @return {@code true} if the cache is enabled, {@code false} otherwise.
+     */
+    boolean enabled( String cacheName );
+
+    /**
+     * Retrieves cache usage information.
+     *
+     * @param cacheName The cache to be queried.
+     * @return cache usage information, or {@code null} if the requested cache 
is not enabled.
+     */
+    CacheInfo info( String cacheName );
+
+    /**
+     * Returns the list of keys from elements present in a cache.
+     *
+     * @param cacheName The cache to be queried.
+     * @return list of keys from elements present in a cache.
+     */
+    < T extends Serializable > List< T > keys( String cacheName );
+
+    /**
+     * Returns an item from a cache. If it is not found on the cache, try to 
retrieve from the provided supplier. If
+     * found there, put the value in the cache, and return it. Otherwise, 
return {@code null}.
+     *
+     * @param cacheName The cache in which the item lives.
+     * @param key item's identifier.
+     * @param supplier if the element is not cached, try to retrieve from the 
cached system.
+     * @throws E the supplier may throw a checked exception, which is 
propagated upwards.
+     * @return The requested item or {@code null} if either the cache is not 
enabled or the item is not present on the cache / cached service.
+     */
+    < T, E extends Exception > T get( String cacheName, Serializable key, 
CheckedSupplier< T, E > supplier ) throws E;
+
+    /**
+     * Puts an item on a cache.
+     *
+     * @param cacheName The cache in which the item will live.
+     * @param key item's identifier.
+     * @param val item to insert in the cache.
+     */
+    void put( String cacheName, Serializable key, Object val );
+
+    /**
+     * Removes an item from a cache.
+     *
+     * @param cacheName The cache in which the item to be removed lives.
+     * @param key item's identifier.
+     */
+    void remove( String cacheName, Serializable key );
+
+}
diff --git 
a/jspwiki-cache/src/main/java/org/apache/wiki/cache/EhcacheCachingManager.java 
b/jspwiki-cache/src/main/java/org/apache/wiki/cache/EhcacheCachingManager.java
new file mode 100644
index 0000000..6e6942d
--- /dev/null
+++ 
b/jspwiki-cache/src/main/java/org/apache/wiki/cache/EhcacheCachingManager.java
@@ -0,0 +1,166 @@
+/*
+    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.
+ */
+package org.apache.wiki.cache;
+
+import net.sf.ehcache.Cache;
+import net.sf.ehcache.CacheManager;
+import net.sf.ehcache.Element;
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
+import org.apache.wiki.api.core.Engine;
+import org.apache.wiki.api.engine.Initializable;
+import org.apache.wiki.api.exceptions.WikiException;
+import org.apache.wiki.util.CheckedSupplier;
+import org.apache.wiki.util.TextUtil;
+
+import java.io.Serializable;
+import java.net.URL;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+import java.util.Properties;
+import java.util.concurrent.ConcurrentHashMap;
+
+
+/**
+ * Ehcache-based {@link CachingManager}.
+ */
+public class EhcacheCachingManager implements CachingManager, Initializable {
+
+    private static final Logger LOG = LogManager.getLogger( 
EhcacheCachingManager.class );
+    private static final int DEFAULT_CACHE_SIZE = 1_000;
+    private static final int DEFAULT_CACHE_EXPIRY_PERIOD = 24*60*60;
+
+    final Map< String, Cache > cacheMap = new ConcurrentHashMap<>();
+    final Map< String, CacheInfo > cacheStats = new ConcurrentHashMap<>();
+    CacheManager cacheManager;
+
+    /** {@inheritDoc} */
+    @Override
+    public void shutdown() {
+        if( cacheMap.size() > 0 ) {
+            CacheManager.getInstance().shutdown();
+            cacheMap.clear();
+            cacheStats.clear();
+        }
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public void initialize( final Engine engine, final Properties props ) 
throws WikiException {
+        final String cacheEnabled = TextUtil.getStringProperty( props, 
PROP_CACHE_ENABLE, PROP_USECACHE_DEPRECATED, "true" );
+        final boolean useCache = "true".equalsIgnoreCase( cacheEnabled );
+        final String confLocation = "/" + TextUtil.getStringProperty( props, 
PROP_CACHE_CONF_FILE, "ehcache-jspwiki.xml" );
+        if( useCache ) {
+            final URL location = this.getClass().getResource( confLocation );
+            LOG.info( "Reading ehcache configuration file from classpath on 
/{}", location );
+            cacheManager = CacheManager.create( location );
+            registerCache( CACHE_ATTACHMENTS );
+            registerCache( CACHE_ATTACHMENTS_COLLECTION );
+            registerCache( CACHE_ATTACHMENTS_DYNAMIC );
+            registerCache( CACHE_DOCUMENTS );
+            registerCache( CACHE_PAGES );
+            registerCache( CACHE_PAGES_HISTORY );
+            registerCache( CACHE_PAGES_TEXT );
+        }
+    }
+
+    void registerCache( final String cacheName ) {
+        final Cache cache;
+        if( cacheManager.cacheExists( cacheName ) ) {
+            cache = cacheManager.getCache( cacheName );
+        } else {
+            LOG.info( "cache with name {} not found in ehcache configuration 
file, creating it with defaults.", cacheName );
+            cache = new Cache( cacheName, DEFAULT_CACHE_SIZE, false, false, 
DEFAULT_CACHE_EXPIRY_PERIOD, DEFAULT_CACHE_EXPIRY_PERIOD );
+            cacheManager.addCache( cache );
+        }
+        cacheMap.put( cacheName, cache );
+        cacheStats.put( cacheName, new CacheInfo( cacheName, 
cache.getCacheConfiguration().getMaxEntriesLocalHeap() ) );
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public boolean enabled( final String cacheName ) {
+        return cacheMap.get( cacheName ) != null;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public CacheInfo info( final String cacheName ) {
+        if( enabled( cacheName ) ) {
+            return cacheStats.get( cacheName );
+        }
+        return null;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    @SuppressWarnings( "unchecked" )
+    public List< String > keys( final String cacheName ) {
+        if( enabled( cacheName ) ) {
+            return cacheMap.get( cacheName ).getKeysWithExpiryCheck();
+        }
+        return Collections.emptyList();
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    @SuppressWarnings( "unchecked" )
+    public < T, E extends Exception > T get( final String cacheName, final 
Serializable key, final CheckedSupplier< T, E > supplier ) throws E {
+        if( keyAndCacheAreNotNull( cacheName, key ) ) {
+            final Element element = cacheMap.get( cacheName ).get( key );
+            if( element != null ) {
+                cacheStats.get( cacheName ).hit();
+                return ( T )element.getObjectValue();
+            } else {
+                // element doesn't exist in cache, try to retrieve from the 
cached service instead.
+                final T value = supplier.get();
+                if( value != null ) {
+                    cacheStats.get( cacheName ).miss();
+                    cacheMap.get( cacheName ).put( new Element( key, value ) );
+                }
+                return value;
+            }
+        }
+        return null;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public void put( final String cacheName, final Serializable key, final 
Object val ) {
+        if( keyAndCacheAreNotNull( cacheName, key ) ) {
+            cacheMap.get( cacheName ).put( new Element( key, val ) );
+        }
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public void remove( final String cacheName, final Serializable key ) {
+        if( keyAndCacheAreNotNull( cacheName, key ) ) {
+            cacheMap.get( cacheName ).remove( key );
+        }
+    }
+
+    boolean keyAndCacheAreNotNull( final String cacheName, final Serializable 
key ) {
+        return enabled( cacheName ) && key != null;
+    }
+
+}
diff --git a/jspwiki-cache/src/main/java/org/apache/wiki/cache/package.html 
b/jspwiki-cache/src/main/java/org/apache/wiki/cache/package.html
new file mode 100644
index 0000000..ad29a2c
--- /dev/null
+++ b/jspwiki-cache/src/main/java/org/apache/wiki/cache/package.html
@@ -0,0 +1,29 @@
+<!--
+    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.  
+-->
+
+<body>
+
+Provides a cache abstraction for JSPWiki.
+
+<h2>Package Specification</h2>
+
+Contains a Caching facade, with the default Ehcache-based implementation.
+
+<hr>
+</body>
diff --git a/jspwiki-main/src/main/resources/ehcache.xml 
b/jspwiki-cache/src/main/resources/ehcache-jspwiki.xml
similarity index 84%
copy from jspwiki-main/src/main/resources/ehcache.xml
copy to jspwiki-cache/src/main/resources/ehcache-jspwiki.xml
index 4ae77a4..bc47135 100644
--- a/jspwiki-main/src/main/resources/ehcache.xml
+++ b/jspwiki-cache/src/main/resources/ehcache-jspwiki.xml
@@ -58,20 +58,14 @@
                                      First In First Out (specified as FIFO) 
and Less Frequently Used
                                      (specified as LFU)
     -->
-    <!-- the default JSPWiki caches -->
-    <cache name="JSPWiki.jspwiki.renderingCache" maxElementsInMemory="1000" />
-
-    <cache name="JSPWiki.jspwiki.pageCache" maxElementsInMemory="1000" />
-
-    <cache name="JSPWiki.jspwiki.pageTextCache" maxElementsInMemory="1000" />
-
-    <cache name="JSPWiki.jspwiki.pageHistoryCache" maxElementsInMemory="1000" 
/>
 
-    <cache name="JSPWiki.jspwiki.attachmentsCache" maxElementsInMemory="1000" 
/>
-
-    <cache name="JSPWiki.jspwiki.attachmentCollectionsCache" 
maxElementsInMemory="1000" />
-
-    <cache name="JSPWiki.jspwiki.dynamicAttachmentCache" 
maxElementsInMemory="1000" />
+    <!-- the default JSPWiki caches -->
+    <cache name="jspwiki.renderingCache" maxElementsInMemory="1000" />
+    <cache name="jspwiki.pageCache" maxElementsInMemory="1000" />
+    <cache name="jspwiki.pageTextCache" maxElementsInMemory="1000" />
+    <cache name="jspwiki.pageHistoryCache" maxElementsInMemory="1000" />
+    <cache name="jspwiki.attachmentsCache" maxElementsInMemory="1000" />
+    <cache name="jspwiki.attachmentCollectionsCache" 
maxElementsInMemory="1000" />
+    <cache name="jspwiki.dynamicAttachmentCache" maxElementsInMemory="1000" />
 
-    <cache name="JSPWiki.jspwiki.rssCache" maxElementsInMemory="250" />
 </ehcache>
diff --git 
a/jspwiki-cache/src/test/java/org/apache/wiki/cache/EhcacheCachingManagerTest.java
 
b/jspwiki-cache/src/test/java/org/apache/wiki/cache/EhcacheCachingManagerTest.java
new file mode 100644
index 0000000..90441f5
--- /dev/null
+++ 
b/jspwiki-cache/src/test/java/org/apache/wiki/cache/EhcacheCachingManagerTest.java
@@ -0,0 +1,85 @@
+/*
+    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.
+ */
+package org.apache.wiki.cache;
+
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.BeforeAll;
+import org.junit.jupiter.api.Test;
+
+import java.util.Properties;
+
+
+public class EhcacheCachingManagerTest {
+
+    static EhcacheCachingManager ecm = new EhcacheCachingManager();
+
+    @BeforeAll
+    static void beforeAll() throws Exception {
+        ecm.initialize( null, new Properties() );
+    }
+
+    @Test
+    void testInitAndShutdown() throws Exception {
+        final Properties props = new Properties();
+        props.setProperty( CachingManager.PROP_CACHE_CONF_FILE, 
"ehcache-jspwiki-test.xml" );
+        EhcacheCachingManager ecm = new EhcacheCachingManager();
+        ecm.initialize( null, props );
+        Assertions.assertEquals( 7, ecm.cacheMap.size() );
+
+        ecm.registerCache( "anotherCache" );
+        Assertions.assertEquals( 8, ecm.cacheMap.size() );
+
+        ecm.shutdown();
+        ecm.shutdown(); // does nothing if already shutdown
+        Assertions.assertEquals( 0, ecm.cacheMap.size() );
+
+        props.setProperty( CachingManager.PROP_CACHE_ENABLE, "false" ); ecm = 
new EhcacheCachingManager();
+        ecm.initialize( null, props );
+        Assertions.assertEquals( 0, ecm.cacheMap.size() );
+    }
+
+    @Test
+    void testEnabled() {
+        Assertions.assertTrue( ecm.enabled( CachingManager.CACHE_PAGES ) );
+        Assertions.assertFalse( ecm.enabled( "Trucutru" ) );
+    }
+
+    @Test
+    void testInfo() {
+        Assertions.assertNotNull( ecm.info( CachingManager.CACHE_PAGES ) );
+        Assertions.assertNull( ecm.info( "Trucutru" ) );
+    }
+
+    @Test
+    void testPutGetRemoveAndKeys() {
+        final String retrieveFromBackend = "item";
+        ecm.put( CachingManager.CACHE_PAGES, "key", "test" );
+        ecm.put( "trucutru", "key", "test" );
+        Assertions.assertEquals( "test", ecm.get( CachingManager.CACHE_PAGES, 
"key", () -> retrieveFromBackend ) );
+        ecm.remove( CachingManager.CACHE_PAGES, "key" );
+        ecm.remove( CachingManager.CACHE_PAGES, null );
+        Assertions.assertEquals( "item", ecm.get( CachingManager.CACHE_PAGES, 
"key", () -> retrieveFromBackend ) );
+        Assertions.assertEquals( 1, ecm.keys( CachingManager.CACHE_PAGES 
).size() );
+        Assertions.assertEquals( 0, ecm.keys( "trucutru" ).size() );
+
+        Assertions.assertNull( ecm.get( CachingManager.CACHE_PAGES, null,  () 
-> retrieveFromBackend ) );
+        Assertions.assertNull( ecm.get( "trucutru", "key",  () -> 
retrieveFromBackend ) );
+    }
+
+}
diff --git a/jspwiki-main/src/main/resources/ehcache.xml 
b/jspwiki-cache/src/test/resources/ehcache-jspwiki-test.xml
similarity index 83%
rename from jspwiki-main/src/main/resources/ehcache.xml
rename to jspwiki-cache/src/test/resources/ehcache-jspwiki-test.xml
index 4ae77a4..4f97530 100644
--- a/jspwiki-main/src/main/resources/ehcache.xml
+++ b/jspwiki-cache/src/test/resources/ehcache-jspwiki-test.xml
@@ -58,20 +58,10 @@
                                      First In First Out (specified as FIFO) 
and Less Frequently Used
                                      (specified as LFU)
     -->
-    <!-- the default JSPWiki caches -->
-    <cache name="JSPWiki.jspwiki.renderingCache" maxElementsInMemory="1000" />
 
-    <cache name="JSPWiki.jspwiki.pageCache" maxElementsInMemory="1000" />
+    <cache name="jspwiki.renderingCache" maxElementsInMemory="1" />
+    <cache name="jspwiki.pageCache" maxElementsInMemory="1" />
+    <cache name="jspwiki.pageTextCache" maxElementsInMemory="1" />
+    <cache name="notManagedByJSPWikiCache" maxElementsInMemory="100" />
 
-    <cache name="JSPWiki.jspwiki.pageTextCache" maxElementsInMemory="1000" />
-
-    <cache name="JSPWiki.jspwiki.pageHistoryCache" maxElementsInMemory="1000" 
/>
-
-    <cache name="JSPWiki.jspwiki.attachmentsCache" maxElementsInMemory="1000" 
/>
-
-    <cache name="JSPWiki.jspwiki.attachmentCollectionsCache" 
maxElementsInMemory="1000" />
-
-    <cache name="JSPWiki.jspwiki.dynamicAttachmentCache" 
maxElementsInMemory="1000" />
-
-    <cache name="JSPWiki.jspwiki.rssCache" maxElementsInMemory="250" />
 </ehcache>
diff --git a/jspwiki-main/src/main/resources/ini/classmappings.xml 
b/jspwiki-main/src/main/resources/ini/classmappings.xml
index 6cff675..bd5c91b 100644
--- a/jspwiki-main/src/main/resources/ini/classmappings.xml
+++ b/jspwiki-main/src/main/resources/ini/classmappings.xml
@@ -88,6 +88,10 @@
     
<mappedClass>org.apache.wiki.auth.authorize.DefaultGroupManager</mappedClass>
   </mapping>
   <mapping>
+    <requestedClass>org.apache.wiki.cache.CachingManager</requestedClass>
+    <mappedClass>org.apache.wiki.cache.EhcacheCachingManager</mappedClass>
+  </mapping>
+  <mapping>
     <requestedClass>org.apache.wiki.content.PageRenamer</requestedClass>
     <mappedClass>org.apache.wiki.content.DefaultPageRenamer</mappedClass>
   </mapping>
diff --git a/jspwiki-main/src/main/resources/ini/jspwiki.properties 
b/jspwiki-main/src/main/resources/ini/jspwiki.properties
index 1f0ee8a..7a54fe7 100644
--- a/jspwiki-main/src/main/resources/ini/jspwiki.properties
+++ b/jspwiki-main/src/main/resources/ini/jspwiki.properties
@@ -77,7 +77,9 @@ jspwiki.pageProvider = FileSystemProvider
 #
 #  NB: This replaces the JSPWiki 1.x "CachingProvider" setting, since it 
probably was too confusing.
 #
-jspwiki.usePageCache = true
+jspwiki.cache.enable = true
+# This property superseedes
+#jspwiki.usePageCache = true (which has been thus deprecated)
 
 #
 # Specify file from classpath that holds ehcache configuration.
diff --git a/jspwiki-main/src/test/resources/ini/jspwiki.properties 
b/jspwiki-main/src/test/resources/ini/jspwiki.properties
index 4e93ae1..cdc4fb5 100644
--- a/jspwiki-main/src/test/resources/ini/jspwiki.properties
+++ b/jspwiki-main/src/test/resources/ini/jspwiki.properties
@@ -17,7 +17,7 @@
 #
 jspwiki.applicationName = JSPWiki
 jspwiki.pageProvider = FileSystemProvider
-jspwiki.usePageCache = true
+jspwiki.cache.enable = true
 jspwiki.attachmentProvider = BasicAttachmentProvider
 jspwiki.diffProvider = TraditionalDiffProvider
 jspwiki.encoding = UTF-8
diff --git 
a/jspwiki-portable/src/overlay/woas/webapps/personal/WEB-INF/classes/ehcache.xml
 
b/jspwiki-portable/src/overlay/woas/webapps/department/WEB-INF/classes/ehcache-woas-department.xml
similarity index 100%
rename from 
jspwiki-portable/src/overlay/woas/webapps/personal/WEB-INF/classes/ehcache.xml
rename to 
jspwiki-portable/src/overlay/woas/webapps/department/WEB-INF/classes/ehcache-woas-department.xml
diff --git 
a/jspwiki-portable/src/overlay/woas/webapps/department/WEB-INF/classes/jspwiki-custom.properties
 
b/jspwiki-portable/src/overlay/woas/webapps/department/WEB-INF/classes/jspwiki-custom.properties
index 01cb186..f712c85 100644
--- 
a/jspwiki-portable/src/overlay/woas/webapps/department/WEB-INF/classes/jspwiki-custom.properties
+++ 
b/jspwiki-portable/src/overlay/woas/webapps/department/WEB-INF/classes/jspwiki-custom.properties
@@ -30,7 +30,8 @@ jspwiki.attachment.maxsize=4000000
 jspwiki.breakTitleWithSpaces=true
 jspwiki.searchProvider=LuceneSearchProvider
 jspwiki.security=jaas
-jspwiki.usePageCache=true
+jspwiki.cache.enable=true
+jspwiki.cache.config-file=ehcache-woas-department.xml
 
 # jspwiki.xmlGroupDatabaseFile=/usr/local/tomcat/lib/groupdatabase.xml
 # jspwiki.xmlUserDatabaseFile=/usr/local/tomcat/lib/userdatabase.xml
diff --git 
a/jspwiki-portable/src/overlay/woas/webapps/department/WEB-INF/classes/ehcache.xml
 
b/jspwiki-portable/src/overlay/woas/webapps/personal/WEB-INF/classes/ehcache-woas-personal.xml
similarity index 100%
rename from 
jspwiki-portable/src/overlay/woas/webapps/department/WEB-INF/classes/ehcache.xml
rename to 
jspwiki-portable/src/overlay/woas/webapps/personal/WEB-INF/classes/ehcache-woas-personal.xml
diff --git 
a/jspwiki-portable/src/overlay/woas/webapps/personal/WEB-INF/classes/jspwiki-custom.properties
 
b/jspwiki-portable/src/overlay/woas/webapps/personal/WEB-INF/classes/jspwiki-custom.properties
index d23c2bc..dd33774 100644
--- 
a/jspwiki-portable/src/overlay/woas/webapps/personal/WEB-INF/classes/jspwiki-custom.properties
+++ 
b/jspwiki-portable/src/overlay/woas/webapps/personal/WEB-INF/classes/jspwiki-custom.properties
@@ -30,7 +30,8 @@ jspwiki.breakTitleWithSpaces=true
 jspwiki.pageProvider=FileSystemProvider
 jspwiki.searchProvider=BasicSearchProvider
 jspwiki.security=jaas
-jspwiki.usePageCache=true
+jspwiki.cache.enable=true
+jspwiki.cache.config-file=ehcache-woas-personal.xml
 
 # jspwiki.xmlGroupDatabaseFile=/usr/local/tomcat/lib/groupdatabase.xml
 # jspwiki.xmlUserDatabaseFile=/usr/local/tomcat/lib/userdatabase.xml
diff --git 
a/jspwiki-util/src/main/java/org/apache/wiki/util/CheckedSupplier.java 
b/jspwiki-util/src/main/java/org/apache/wiki/util/CheckedSupplier.java
new file mode 100644
index 0000000..6ee0a1f
--- /dev/null
+++ b/jspwiki-util/src/main/java/org/apache/wiki/util/CheckedSupplier.java
@@ -0,0 +1,39 @@
+/*
+    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.
+ */
+package org.apache.wiki.util;
+
+
+/**
+ * A {@code Supplier} cousin that also throws Checked Exceptions.
+ *
+ * @param <T> type returned by this functional interface.
+ * @param <E> {@link Exception} thrown by this functional interface.
+ */
+@FunctionalInterface
+public interface CheckedSupplier< T, E extends Exception > {
+
+    /**
+     * Gets a result.
+     *
+     * @throws E a checked exception.
+     * @return a result.
+     */
+    T get() throws E;
+
+}
diff --git a/jspwiki-util/src/main/java/org/apache/wiki/util/TextUtil.java 
b/jspwiki-util/src/main/java/org/apache/wiki/util/TextUtil.java
index 68a2498..49115aa 100644
--- a/jspwiki-util/src/main/java/org/apache/wiki/util/TextUtil.java
+++ b/jspwiki-util/src/main/java/org/apache/wiki/util/TextUtil.java
@@ -19,6 +19,8 @@
 package org.apache.wiki.util;
 
 import org.apache.commons.lang3.StringUtils;
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
 
 import java.io.File;
 import java.io.IOException;
@@ -35,9 +37,11 @@ import java.util.Random;
  */
 public final class TextUtil {
 
+    private static final Logger LOG = LogManager.getLogger( TextUtil.class );
+
     static final String HEX_DIGITS = "0123456789ABCDEF";
 
-    /** Pick from some letters that won't be easily mistaken for each other to 
compose passwords. So, for example, omit o O and 0, 1 l and L.*/
+    /** Pick from some letters that won't be easily mistaken for each other to 
compose passwords. So, for example, omit o, O and 0, or 1, l and L.*/
     static final String PWD_BASE = 
"abcdefghjkmnpqrstuvwxyzABCDEFGHJKMNPQRSTUVWXYZ23456789+@";
 
     /** Length of password. {@link #generateRandomPassword() */
@@ -170,7 +174,7 @@ public final class TextUtil {
      *
      * @param data A string to encode
      * @param encoding The encoding in which to encode
-     * @return An URL encoded string.
+     * @return A URL encoded string.
      */
     public static String urlEncode( final String data, final String encoding ) 
{
         // Presumably, the same caveats apply as in FileSystemProvider. Don't 
see why it would be horribly kludgy, though.
@@ -217,7 +221,7 @@ public final class TextUtil {
     }
 
     /**
-     *  Replaces a string with an other string.
+     *  Replaces a string with another string.
      *
      *  @param orig Original string.  Null is safe.
      *  @param src  The string to find.
@@ -271,11 +275,11 @@ public final class TextUtil {
     }
 
     /**
-     *  Replaces a string with an other string. Case insensitive matching is 
used
+     *  Replaces a string with another string. Case-insensitive matching is 
used
      *
      *  @param orig Original string.  Null is safe.
      *  @param src  The string to find.
-     *  @param dest The string to replace <I>src</I> with.
+     *  @param dest The string to replace <em>src</em> with.
      *  @return A string with all instances of src replaced with dest.
      */
     public static String replaceStringCaseUnsensitive( final String orig, 
final String src, final String dest ) {
@@ -368,18 +372,18 @@ public final class TextUtil {
     }
 
     /**
-     *  Fetches a String property from the set of Properties.  This differs 
from Properties.getProperty() in a
-     *  couple of key respects: First, property value is trim()med (so no 
extra whitespace back and front).
-     *
-     *  Before inspecting the props, we first check if there is a Java System 
Property with the same name, if it exists
-     *  we use that value, if not we check an environment variable with that 
(almost) same name, almost meaning we replace
-     *  dots with underscores.
-     *
-     *  @param props The Properties to search through
-     *  @param key   The property key
-     *  @param defval A default value to return, if the property does not 
exist.
-     *  @return The property value.
-     *  @since 2.1.151
+     * Fetches a String property from the set of Properties.  This differs 
from Properties.getProperty() in a
+     * couple of key respects: First, property value is trim()med (so no extra 
whitespace back and front).
+     *
+     * Before inspecting the props, we first check if there is a Java System 
Property with the same name, if it exists
+     * we use that value, if not we check an environment variable with that 
(almost) same name, almost meaning we replace
+     * dots with underscores.
+     *
+     * @param props The Properties to search through
+     * @param key   The property key
+     * @param defval A default value to return, if the property does not exist.
+     * @return The property value.
+     * @since 2.1.151
      */
     public static String getStringProperty( final Properties props, final 
String key, final String defval ) {
         String val = System.getProperties().getProperty( key, System.getenv( 
StringUtils.replace( key,".","_" ) ) );
@@ -393,6 +397,26 @@ public final class TextUtil {
     }
 
     /**
+     * {@link #getStringProperty(Properties, String, String)} overload that 
handles deprecated keys, so that a key and its
+     * deprecated counterpart can coexist in a given version of JSPWiki.
+     *
+     * @param props The Properties to search through
+     * @param key The property key
+     * @param deprecatedKey the property key being superseeded by key
+     * @param defval A default value to return, if the property does not exist.
+     * @return The property value.
+     */
+    public static String getStringProperty( final Properties props, final 
String key, final String deprecatedKey, final String defval ) {
+        final String val = getStringProperty( props, deprecatedKey, null );
+        if( val != null ) {
+            LOG.warn( "{} is being deprecated and will be removed on a future 
version, please consider using {} instead" +
+                      "in your jspwiki[-custom].properties file", key, key );
+            return val;
+        }
+        return getStringProperty( props, key, defval );
+    }
+
+    /**
      *  Throws an exception if a property is not found.
      *
      *  @param props A set of properties to search the key in.
@@ -470,7 +494,7 @@ public final class TextUtil {
      *  <LI>The CR/LF/CRLF mess is normalized to plain CRLF.
      *  </UL>
      *
-     *  The reason why we're using CRLF is that most browser already return 
CRLF since that is the closest thing to a HTTP standard.
+     *  The reason why we're using CRLF is that most browser already return 
CRLF since that is the closest thing to an HTTP standard.
      *
      *  @param postData The data to normalize
      *  @return Normalized data
diff --git a/jspwiki-util/src/test/java/org/apache/wiki/util/TextUtilTest.java 
b/jspwiki-util/src/test/java/org/apache/wiki/util/TextUtilTest.java
index 0bc4a81..812bca7 100644
--- a/jspwiki-util/src/test/java/org/apache/wiki/util/TextUtilTest.java
+++ b/jspwiki-util/src/test/java/org/apache/wiki/util/TextUtilTest.java
@@ -292,6 +292,15 @@ public class TextUtilTest {
     }
 
     @Test
+    public void testGetStringPropertyDeprecated() {
+        final String[] vals = { "foo", " this is a property ", "foo-dep", 
"deprecated" };
+        final Properties props = TextUtil.createProperties(vals);
+        Assertions.assertEquals( "deprecated", TextUtil.getStringProperty( 
props, "foo", "foo-dep", "err" ) );
+        Assertions.assertEquals( "this is a property", 
TextUtil.getStringProperty( props, "foo", "bar-dep", "err" ) );
+        Assertions.assertEquals( "err", TextUtil.getStringProperty( props, 
"fooo", "bar-dep", "err" ) );
+    }
+
+    @Test
     public void testGetStringPropertyDefaultValue() {
         final String defaultValue = System.getProperty( "user.home" ) + 
File.separator + "jspwiki-files";
         final String[] vals = { "foo", " this is a property " };
diff --git a/jspwiki-util/src/test/resources/ini/jspwiki.properties 
b/jspwiki-util/src/test/resources/ini/jspwiki.properties
index a45cf7b..93a5adc 100644
--- a/jspwiki-util/src/test/resources/ini/jspwiki.properties
+++ b/jspwiki-util/src/test/resources/ini/jspwiki.properties
@@ -17,7 +17,7 @@
 #
 jspwiki.applicationName = JSPWiki
 jspwiki.pageProvider = FileSystemProvider
-jspwiki.usePageCache = true
+jspwiki.cache.enable = true
 jspwiki.attachmentProvider = BasicAttachmentProvider
 jspwiki.diffProvider = TraditionalDiffProvider
 jspwiki.encoding = UTF-8

Reply via email to