001/* 002 Licensed to the Apache Software Foundation (ASF) under one 003 or more contributor license agreements. See the NOTICE file 004 distributed with this work for additional information 005 regarding copyright ownership. The ASF licenses this file 006 to you under the Apache License, Version 2.0 (the 007 "License"); you may not use this file except in compliance 008 with the License. You may obtain a copy of the License at 009 010 http://www.apache.org/licenses/LICENSE-2.0 011 012 Unless required by applicable law or agreed to in writing, 013 software distributed under the License is distributed on an 014 "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 015 KIND, either express or implied. See the License for the 016 specific language governing permissions and limitations 017 under the License. 018 */ 019package org.apache.wiki.cache; 020 021import net.sf.ehcache.Cache; 022import net.sf.ehcache.CacheManager; 023import net.sf.ehcache.Element; 024import org.apache.logging.log4j.LogManager; 025import org.apache.logging.log4j.Logger; 026import org.apache.wiki.api.core.Engine; 027import org.apache.wiki.api.engine.Initializable; 028import org.apache.wiki.api.exceptions.WikiException; 029import org.apache.wiki.util.CheckedSupplier; 030import org.apache.wiki.util.TextUtil; 031 032import java.io.Serializable; 033import java.net.URL; 034import java.util.Collections; 035import java.util.List; 036import java.util.Map; 037import java.util.Properties; 038import java.util.concurrent.ConcurrentHashMap; 039 040 041/** 042 * Ehcache-based {@link CachingManager}. 043 */ 044public class EhcacheCachingManager implements CachingManager, Initializable { 045 046 private static final Logger LOG = LogManager.getLogger( EhcacheCachingManager.class ); 047 private static final int DEFAULT_CACHE_SIZE = 1_000; 048 private static final int DEFAULT_CACHE_EXPIRY_PERIOD = 24*60*60; 049 050 final Map< String, Cache > cacheMap = new ConcurrentHashMap<>(); 051 final Map< String, CacheInfo > cacheStats = new ConcurrentHashMap<>(); 052 CacheManager cacheManager; 053 054 /** {@inheritDoc} */ 055 @Override 056 public void shutdown() { 057 if( cacheMap.size() > 0 ) { 058 CacheManager.getInstance().shutdown(); 059 cacheMap.clear(); 060 cacheStats.clear(); 061 } 062 } 063 064 /** {@inheritDoc} */ 065 @Override 066 public void initialize( final Engine engine, final Properties props ) throws WikiException { 067 final String cacheEnabled = TextUtil.getStringProperty( props, PROP_CACHE_ENABLE, PROP_USECACHE_DEPRECATED, "true" ); 068 final boolean useCache = "true".equalsIgnoreCase( cacheEnabled ); 069 final String confLocation = "/" + TextUtil.getStringProperty( props, PROP_CACHE_CONF_FILE, "ehcache-jspwiki.xml" ); 070 if( useCache ) { 071 final URL location = this.getClass().getResource( confLocation ); 072 LOG.info( "Reading ehcache configuration file from classpath on /{}", location ); 073 cacheManager = CacheManager.create( location ); 074 registerCache( CACHE_ATTACHMENTS ); 075 registerCache( CACHE_ATTACHMENTS_COLLECTION ); 076 registerCache( CACHE_ATTACHMENTS_DYNAMIC ); 077 registerCache( CACHE_DOCUMENTS ); 078 registerCache( CACHE_PAGES ); 079 registerCache( CACHE_PAGES_HISTORY ); 080 registerCache( CACHE_PAGES_TEXT ); 081 } 082 } 083 084 void registerCache( final String cacheName ) { 085 final Cache cache; 086 if( cacheManager.cacheExists( cacheName ) ) { 087 cache = cacheManager.getCache( cacheName ); 088 } else { 089 LOG.info( "cache with name {} not found in ehcache configuration file, creating it with defaults.", cacheName ); 090 cache = new Cache( cacheName, DEFAULT_CACHE_SIZE, false, false, DEFAULT_CACHE_EXPIRY_PERIOD, DEFAULT_CACHE_EXPIRY_PERIOD ); 091 cacheManager.addCache( cache ); 092 } 093 cacheMap.put( cacheName, cache ); 094 cacheStats.put( cacheName, new CacheInfo( cacheName, cache.getCacheConfiguration().getMaxEntriesLocalHeap() ) ); 095 } 096 097 /** {@inheritDoc} */ 098 @Override 099 public boolean enabled( final String cacheName ) { 100 return cacheMap.get( cacheName ) != null; 101 } 102 103 /** {@inheritDoc} */ 104 @Override 105 public CacheInfo info( final String cacheName ) { 106 if( enabled( cacheName ) ) { 107 return cacheStats.get( cacheName ); 108 } 109 return null; 110 } 111 112 /** 113 * {@inheritDoc} 114 */ 115 @Override 116 @SuppressWarnings( "unchecked" ) 117 public List< String > keys( final String cacheName ) { 118 if( enabled( cacheName ) ) { 119 return cacheMap.get( cacheName ).getKeysWithExpiryCheck(); 120 } 121 return Collections.emptyList(); 122 } 123 124 /** {@inheritDoc} */ 125 @Override 126 @SuppressWarnings( "unchecked" ) 127 public < T, E extends Exception > T get( final String cacheName, final Serializable key, final CheckedSupplier< T, E > supplier ) throws E { 128 if( keyAndCacheAreNotNull( cacheName, key ) ) { 129 final Element element = cacheMap.get( cacheName ).get( key ); 130 if( element != null ) { 131 cacheStats.get( cacheName ).hit(); 132 return ( T )element.getObjectValue(); 133 } else { 134 // element doesn't exist in cache, try to retrieve from the cached service instead. 135 final T value = supplier.get(); 136 if( value != null ) { 137 cacheStats.get( cacheName ).miss(); 138 cacheMap.get( cacheName ).put( new Element( key, value ) ); 139 } 140 return value; 141 } 142 } 143 return null; 144 } 145 146 /** {@inheritDoc} */ 147 @Override 148 public void put( final String cacheName, final Serializable key, final Object val ) { 149 if( keyAndCacheAreNotNull( cacheName, key ) ) { 150 cacheMap.get( cacheName ).put( new Element( key, val ) ); 151 } 152 } 153 154 /** {@inheritDoc} */ 155 @Override 156 public void remove( final String cacheName, final Serializable key ) { 157 if( keyAndCacheAreNotNull( cacheName, key ) ) { 158 cacheMap.get( cacheName ).remove( key ); 159 } 160 } 161 162 boolean keyAndCacheAreNotNull( final String cacheName, final Serializable key ) { 163 return enabled( cacheName ) && key != null; 164 } 165 166}