View Javadoc
1   /**
2    * Licensed to the Apache Software Foundation (ASF) under one
3    * or more contributor license agreements. See the NOTICE file
4    * distributed with this work for additional information
5    * regarding copyright ownership. The ASF licenses this file
6    * to you under the Apache License, Version 2.0 (the
7    * "License"); you may not use this file except in compliance
8    * with the License. You may obtain a copy of the License at
9    *
10   * http://www.apache.org/licenses/LICENSE-2.0
11   *
12   * Unless required by applicable law or agreed to in writing,
13   * software distributed under the License is distributed on an
14   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15   * KIND, either express or implied. See the License for the
16   * specific language governing permissions and limitations
17   * under the License.
18   */
19  
20  package org.apache.wss4j.common.cache;
21  
22  import java.io.File;
23  import java.nio.file.Path;
24  import java.time.Instant;
25  
26  import org.apache.wss4j.common.ext.WSSecurityException;
27  import org.ehcache.Cache;
28  import org.ehcache.CacheManager;
29  import org.ehcache.CachePersistenceException;
30  import org.ehcache.PersistentCacheManager;
31  import org.ehcache.Status;
32  import org.ehcache.config.builders.CacheConfigurationBuilder;
33  import org.ehcache.config.builders.CacheManagerBuilder;
34  import org.ehcache.config.builders.ResourcePoolsBuilder;
35  import org.ehcache.config.units.EntryUnit;
36  import org.ehcache.config.units.MemoryUnit;
37  
38  /**
39   * An in-memory EHCache implementation of the ReplayCache interface, that overflows to disk.
40   * The default TTL is 60 minutes and the max TTL is 12 hours.
41   */
42  public class EHCacheReplayCache implements ReplayCache {
43  
44      private static final org.slf4j.Logger LOG =
45              org.slf4j.LoggerFactory.getLogger(EHCacheReplayCache.class);
46  
47      private final Cache<String, EHCacheValue> cache;
48      private final CacheManager cacheManager;
49      private final String key;
50      private final Path diskstorePath;
51      private final boolean persistent;
52  
53      public EHCacheReplayCache(String key) throws WSSecurityException {
54          this(key, null);
55      }
56  
57      public EHCacheReplayCache(String key, Path diskstorePath) throws WSSecurityException {
58          this(key, diskstorePath, 50, 10000, false);
59      }
60  
61      public EHCacheReplayCache(String key, Path diskstorePath, long diskSize, long heapEntries, boolean persistent)
62              throws WSSecurityException {
63          this.key = key;
64          this.diskstorePath = diskstorePath;
65          this.persistent = persistent;
66  
67          // Do some checking on the arguments
68          if (key == null || persistent && diskstorePath == null) {
69              throw new NullPointerException();
70          }
71          if (diskstorePath != null && (diskSize < 5 || diskSize > 10000)) {
72              throw new IllegalArgumentException("The diskSize parameter must be between 5 and 10000 (megabytes)");
73          }
74          if (heapEntries < 100) {
75              throw new IllegalArgumentException("The heapEntries parameter must be greater than 100 (entries)");
76          }
77  
78          try {
79              ResourcePoolsBuilder resourcePoolsBuilder = ResourcePoolsBuilder.newResourcePoolsBuilder()
80                      .heap(heapEntries, EntryUnit.ENTRIES);
81              if (diskstorePath != null) {
82                  resourcePoolsBuilder = resourcePoolsBuilder.disk(diskSize, MemoryUnit.MB, persistent);
83              }
84  
85              CacheConfigurationBuilder<String, EHCacheValue> configurationBuilder =
86                      CacheConfigurationBuilder.newCacheConfigurationBuilder(
87                              String.class, EHCacheValue.class, resourcePoolsBuilder)
88                              .withExpiry(new EHCacheExpiry());
89  
90              if (diskstorePath != null) {
91                  cacheManager = CacheManagerBuilder.newCacheManagerBuilder()
92                          .with(CacheManagerBuilder.persistence(diskstorePath.toFile()))
93                          .withCache(key, configurationBuilder)
94                          .build();
95              } else {
96                  cacheManager = CacheManagerBuilder.newCacheManagerBuilder()
97                          .withCache(key, configurationBuilder)
98                          .build();
99              }
100 
101             cacheManager.init();
102             cache = cacheManager.getCache(key, String.class, EHCacheValue.class);
103         } catch (Exception ex) {
104             LOG.error("Error configuring EHCacheReplayCache: {}", ex.getMessage());
105             throw new WSSecurityException(WSSecurityException.ErrorCode.FAILURE, ex, "replayCacheError");
106         }
107     }
108 
109     /**
110      * Add the given identifier to the cache. It will be cached for a default amount of time.
111      * @param identifier The identifier to be added
112      */
113     public void add(String identifier) {
114         add(identifier, null);
115     }
116 
117     /**
118      * Add the given identifier to the cache to be cached for the given time
119      * @param identifier The identifier to be added
120      * @param expiry A custom expiry time for the identifier. Can be null in which case, the default expiry is used.
121      */
122     public void add(String identifier, Instant expiry) {
123         if (identifier == null || identifier.length() == 0) {
124             return;
125         }
126 
127         cache.put(identifier, new EHCacheValue(identifier, expiry));
128     }
129 
130     /**
131      * Return true if the given identifier is contained in the cache
132      * @param identifier The identifier to check
133      */
134     public boolean contains(String identifier) {
135         if (cache == null) {
136             return false;
137         }
138         EHCacheValue element = cache.get(identifier);
139         return element != null;
140     }
141 
142     // Only exposed for testing
143     EHCacheValue get(String identifier) {
144         return cache.get(identifier);
145     }
146 
147     @Override
148     public synchronized void close() {
149         if (cacheManager.getStatus() == Status.AVAILABLE) {
150             cacheManager.removeCache(key);
151 
152             cacheManager.close();
153 
154             if (!persistent && cacheManager instanceof PersistentCacheManager) {
155                 try {
156                     ((PersistentCacheManager) cacheManager).destroy();
157                 } catch (CachePersistenceException e) {
158                     LOG.debug("Error in shutting down persistent cache", e);
159                 }
160 
161                 // As we're not using a persistent disk store, just delete it - it should be empty after calling
162                 // destroy above
163                 if (diskstorePath != null) {
164                     File file = diskstorePath.toFile();
165                     if (file.exists() && file.canWrite()) {
166                         file.delete();
167                     }
168                 }
169             }
170         }
171     }
172 
173     public void initComplete() {
174     }
175     public void preShutdown() {
176         close();
177     }
178     public void postShutdown() {
179         close();
180     }
181 
182 }