Skip to content

Commit 9b60f70

Browse files
authored
feat: port CachedEnforcer API from Go to Java (#435)
1 parent 29bbc18 commit 9b60f70

File tree

7 files changed

+742
-0
lines changed

7 files changed

+742
-0
lines changed
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
// Copyright 2024 The casbin Authors. All Rights Reserved.
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
package org.casbin.jcasbin.exception;
16+
17+
public class CasbinCacheException extends RuntimeException{
18+
public CasbinCacheException(String message) {
19+
super(message);
20+
}
21+
22+
public CasbinCacheException(String message, Throwable cause) {
23+
super(message, cause);
24+
}
25+
}
Lines changed: 317 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,317 @@
1+
// Copyright 2024 The casbin Authors. All Rights Reserved.
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
package org.casbin.jcasbin.main;
16+
17+
import org.casbin.jcasbin.model.Model;
18+
import org.casbin.jcasbin.persist.Adapter;
19+
import org.casbin.jcasbin.persist.cache.Cache;
20+
import org.casbin.jcasbin.persist.cache.CacheableParam;
21+
import org.casbin.jcasbin.persist.cache.DefaultCache;
22+
23+
import java.time.Duration;
24+
import java.util.List;
25+
import java.util.concurrent.atomic.AtomicBoolean;
26+
import java.util.concurrent.locks.ReadWriteLock;
27+
import java.util.concurrent.locks.ReentrantReadWriteLock;
28+
29+
public class CachedEnforcer extends Enforcer{
30+
31+
private Duration expireTime;
32+
private Cache cache;
33+
private final AtomicBoolean enableCache = new AtomicBoolean(true);
34+
private final static ReadWriteLock READ_WRITE_LOCK = new ReentrantReadWriteLock();
35+
36+
/**
37+
* Default constructor for CachedEnforcer.
38+
* Initializes a new CachedEnforcer with a default cache.
39+
*/
40+
public CachedEnforcer(){
41+
super();
42+
this.cache = new DefaultCache();
43+
}
44+
45+
/**
46+
* Initializes an enforcer with a model file and a policy file.
47+
*
48+
* @param modelPath The path of the model file.
49+
* @param policyFile The path of the policy file.
50+
*/
51+
public CachedEnforcer(String modelPath, String policyFile){
52+
super(modelPath, policyFile);
53+
this.cache = new DefaultCache();
54+
}
55+
56+
/**
57+
* Initializes an enforcer with a model file and a database adapter.
58+
*
59+
* @param modelPath The path of the model file.
60+
* @param adapter The adapter for the database.
61+
*/
62+
public CachedEnforcer(String modelPath, Adapter adapter) {
63+
super(modelPath, adapter);
64+
this.cache = new DefaultCache();
65+
}
66+
67+
/**
68+
* Initializes an enforcer with a model and a database adapter.
69+
*
70+
* @param m The model.
71+
* @param adapter The adapter for the database.
72+
*/
73+
public CachedEnforcer(Model m, Adapter adapter) {
74+
super(m, adapter);
75+
this.cache = new DefaultCache();
76+
}
77+
78+
/**
79+
* Initializes an enforcer with a model.
80+
*
81+
* @param m The model.
82+
*/
83+
public CachedEnforcer(Model m) {
84+
super(m);
85+
this.cache = new DefaultCache();
86+
}
87+
88+
/**
89+
* Initializes an enforcer with a model file.
90+
*
91+
* @param modelPath The path of the model file.
92+
*/
93+
public CachedEnforcer(String modelPath) {
94+
super(modelPath);
95+
this.cache = new DefaultCache();
96+
}
97+
98+
/**
99+
* Initializes an enforcer with a model file, a policy file, and a logging flag.
100+
*
101+
* @param modelPath The path of the model file.
102+
* @param policyFile The path of the policy file.
103+
* @param enableLog Whether to enable logging for Casbin.
104+
*/
105+
public CachedEnforcer(String modelPath, String policyFile, boolean enableLog) {
106+
super(modelPath, policyFile, enableLog);
107+
this.cache = new DefaultCache();
108+
}
109+
110+
/**
111+
* Retrieves the current cache used by this CachedEnforcer.
112+
*
113+
* @return The cache instance.
114+
*/
115+
public Cache getCache() {
116+
return this.cache;
117+
}
118+
119+
/**
120+
* Enforces a policy based on the given request values.
121+
*
122+
* @param rvals The request values, usually in the format of (sub, obj, act).
123+
* @return The result of the enforcement (true or false).
124+
*/
125+
@Override
126+
public boolean enforce(Object... rvals) {
127+
if (!enableCache.get()) {
128+
return super.enforce(rvals);
129+
}
130+
131+
String key = getKey(rvals);
132+
if (key == null) {
133+
return super.enforce(rvals);
134+
}
135+
136+
boolean cachedResult = getCachedResult(key);
137+
if (cachedResult) {
138+
return cachedResult;
139+
}
140+
141+
boolean result = super.enforce(rvals);
142+
setCachedResult(key, result, expireTime);
143+
return result;
144+
}
145+
146+
147+
/**
148+
* Loads policies into the enforcer.
149+
* If caching is enabled, clears the cache before loading policies.
150+
*/
151+
@Override
152+
public void loadPolicy() {
153+
if(enableCache == null || !enableCache.get()){
154+
super.loadPolicy();
155+
} else {
156+
if (enableCache.get()) {
157+
cache.clear();
158+
}
159+
super.loadPolicy();
160+
}
161+
}
162+
163+
/**
164+
* Removes a policy from the enforcer.
165+
*
166+
* @param params The parameters of the policy to be removed.
167+
* @return True if the policy was removed, false otherwise.
168+
*/
169+
public boolean removePolicy(String... params){
170+
if (enableCache.get()) {
171+
String key = getKey(params);
172+
if (key != null) {
173+
cache.delete(key);
174+
}
175+
}
176+
return super.removePolicy(params);
177+
}
178+
179+
/**
180+
* Removes multiple policies from the enforcer.
181+
*
182+
* @param rules The list of policies to be removed.
183+
* @return True if the policies were removed, false otherwise.
184+
*/
185+
@Override
186+
public boolean removePolicies(List<List<String>> rules) {
187+
if (!rules.isEmpty() && enableCache.get()) {
188+
for (List<String> rule : rules) {
189+
String key = getKey(rule.toArray());
190+
cache.delete(key);
191+
}
192+
}
193+
return super.removePolicies(rules);
194+
}
195+
196+
/**
197+
* Retrieves a cached result based on the key.
198+
*
199+
* @param key The cache key.
200+
* @return The cached result, or null if not found.
201+
*/
202+
private boolean getCachedResult(String key) {
203+
READ_WRITE_LOCK.readLock().lock();
204+
try {
205+
return cache.get(key);
206+
}finally {
207+
READ_WRITE_LOCK.readLock().unlock();
208+
}
209+
}
210+
211+
/**
212+
* Sets the expiration time for cached items.
213+
*
214+
* @param expireTime The duration after which cached items will expire.
215+
*/
216+
public void setExpireTime(Duration expireTime) {
217+
this.expireTime = expireTime;
218+
}
219+
220+
/**
221+
* Sets a custom cache implementation.
222+
*
223+
* @param cache The cache instance to use.
224+
*/
225+
public void setCache(Cache cache) {
226+
this.cache = cache;
227+
}
228+
229+
/**
230+
* Stores a result in the cache with an expiration time.
231+
*
232+
* @param key The cache key.
233+
* @param result The result to cache.
234+
* @param expireTime The duration for which the result should be cached.
235+
*/
236+
private void setCachedResult(String key, boolean result, Duration expireTime) {
237+
READ_WRITE_LOCK.writeLock().lock();
238+
try {
239+
cache.set(key, result, expireTime);
240+
} finally {
241+
READ_WRITE_LOCK.writeLock().unlock();
242+
}
243+
}
244+
245+
/**
246+
* Generates a cache key from the given parameters.
247+
*
248+
* @param params The parameters for generating the key.
249+
* @return The generated cache key, or null if invalid parameters are provided.
250+
*/
251+
private String getKey(Object... params) {
252+
StringBuilder keyBuilder = new StringBuilder();
253+
for (Object param : params) {
254+
if (param instanceof String) {
255+
keyBuilder.append(param);
256+
} else if (param instanceof CacheableParam) {
257+
keyBuilder.append(((CacheableParam) param).getCacheKey());
258+
} else {
259+
return null;
260+
}
261+
keyBuilder.append("$$");
262+
}
263+
return keyBuilder.toString();
264+
}
265+
266+
/**
267+
* Retrieves a cache key from the given parameters.
268+
*
269+
* @param params The parameters for generating the key.
270+
* @return The generated cache key as a string.
271+
*/
272+
public String getCacheKey(Object... params) {
273+
StringBuilder key = new StringBuilder();
274+
275+
for (Object param : params) {
276+
if (param instanceof String) {
277+
key.append((String) param);
278+
} else if (param instanceof CacheableParam) {
279+
key.append(((CacheableParam) param).getCacheKey());
280+
} else {
281+
// Return an error identifier
282+
return "";
283+
}
284+
key.append("$$");
285+
}
286+
// Return the constructed key
287+
return key.toString();
288+
}
289+
290+
/**
291+
* Invalidates all cached decisions.
292+
*/
293+
public void invalidateCache() {
294+
READ_WRITE_LOCK.writeLock().lock();
295+
try {
296+
cache.clear();
297+
} finally {
298+
READ_WRITE_LOCK.writeLock().unlock();
299+
}
300+
}
301+
302+
/**
303+
* Clears all policies from the enforcer.
304+
* If caching is enabled, clears the cache before clearing policies.
305+
*/
306+
@Override
307+
public void clearPolicy() {
308+
if (enableCache.get()) {
309+
try {
310+
cache.clear();
311+
} catch (Exception e) {
312+
// Handle the error
313+
}
314+
}
315+
super.clearPolicy();
316+
}
317+
}
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
// Copyright 2024 The casbin Authors. All Rights Reserved.
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
package org.casbin.jcasbin.persist.cache;
16+
17+
public interface Cache {
18+
/**
19+
* Set puts key and value into cache.
20+
* The first extra parameter should be a java.time.LocalDateTime object denoting the expected survival time.
21+
* If survival time equals 0 or less, the key will always be valid.
22+
*
23+
* @param key the key to store
24+
* @param value the value to store
25+
* @param extra additional parameters (e.g., expiration time)
26+
* @return true if successful, false otherwise
27+
*/
28+
boolean set(String key, boolean value, Object... extra);
29+
30+
31+
32+
/**
33+
* Get returns the result for the given key.
34+
* If there's no such key in the cache, Optional.empty() will be returned.
35+
*
36+
* @param key the key to retrieve
37+
* @return an Optional containing the boolean value if present, otherwise Optional.empty()
38+
*/
39+
boolean get(String key);
40+
41+
/**
42+
* Delete removes the specific key from the cache.
43+
* If the key doesn't exist, it returns false.
44+
*
45+
* @param key the key to delete
46+
*/
47+
void delete(String key);
48+
49+
/**
50+
* Clear deletes all items stored in the cache.
51+
*
52+
*/
53+
void clear();
54+
}

0 commit comments

Comments
 (0)