View Javadoc

1   package com.atlassian.user.impl.cache.properties;
2   
3   import com.atlassian.user.Entity;
4   import com.atlassian.user.EntityException;
5   import com.atlassian.user.properties.PropertySetFactory;
6   import com.atlassian.cache.CacheFactory;
7   import com.atlassian.cache.Cache;
8   import com.opensymphony.module.propertyset.PropertySet;
9   import com.opensymphony.module.propertyset.PropertyException;
10  import com.opensymphony.module.propertyset.PropertySetSchema;
11  
12  import java.util.HashMap;
13  import java.util.Map;
14  import java.util.Date;
15  import java.util.Collection;
16  import java.util.Properties;
17  
18  import org.w3c.dom.Document;
19  
20  /**
21   * A PropertySetFactory that wraps another PropertySetFactory and maintains a cache of its {@link PropertySet}s.  This
22   * prevents the need for PropertySets to be looked up repeatedly from the wrapped PropertySetFactory.
23   * Retrieved PropertySets are also wrapped in a CachedPropertySet so that individual property values are cached
24   * instead of repeatedly retrieved.  Both levels of caching rewrite to the cache whenever a setter is
25   * called on a {@link PropertySet}.
26   *
27   * <p>The cache is keyed purely on entity name, so it assumes all entities
28   * used have unique names, even across entity types.<p>
29   *
30   * <p>Since the decorated PropertySets retrieved from the <code>underlyingPropertySetFactory</code> are cached
31   * they must be {@link java.io.Serializable}.</p>
32   */
33  public class CachingPropertySetFactory implements PropertySetFactory
34  {
35      private final PropertySetFactory underlyingPropertySetFactory;
36      private final CacheFactory cacheFactory;
37  
38      public CachingPropertySetFactory(PropertySetFactory underlyingPropertySetFactory, CacheFactory cacheFactory)
39      {
40          this.underlyingPropertySetFactory = underlyingPropertySetFactory;
41          this.cacheFactory = cacheFactory;
42      }
43  
44      public PropertySet getPropertySet(Entity entity) throws EntityException
45      {
46          Cache cache = getCache();
47          CachedPropertySet cachedPropertySet = (CachedPropertySet) cache.get(entity.getName());
48          if (cachedPropertySet == null)
49          {
50              PropertySet underlyingPropertySet = underlyingPropertySetFactory.getPropertySet(entity);
51              HashMap<String, PropertySet> args = new HashMap<String, PropertySet>();
52              args.put("PropertySet", underlyingPropertySet);
53              cachedPropertySet = new CachedPropertySet();
54              cachedPropertySet.init(args, args);
55  
56              cache.put(entity.getName(), cachedPropertySet);
57          }
58          return new RecacheOnWritePropertySet(cachedPropertySet, entity.getName());
59      }
60  
61      private Cache getCache()
62      {
63          String cacheName = underlyingPropertySetFactory.getClass().getName() + ".propertysets";
64          return cacheFactory.getCache(cacheName);
65      }
66  
67      /**
68       * PropertySet Wrapper that propagates any changes in the wrapped PropertySet into the cache.
69       * This is necessary because some caches do not update correctly when a cached object is modified.
70       */
71      private class RecacheOnWritePropertySet implements PropertySet
72      {
73          // Every {@link PropertySet} setter in this object must recache the wrapped delegate
74          // into the cache.
75  
76          private final PropertySet delegate;
77          private String cacheKey;
78  
79          public RecacheOnWritePropertySet(PropertySet delegate, String cacheKey)
80          {
81              this.delegate = delegate;
82              this.cacheKey = cacheKey;
83          }
84  
85          public void init(Map config, Map args)
86          {
87              delegate.init(config, args);
88              recache();
89          }
90  
91          public void setAsActualType(String key, Object value) throws PropertyException
92          {
93              delegate.setAsActualType(key, value);
94              recache();
95          }
96  
97          public Object getAsActualType(String key) throws PropertyException
98          {
99              return delegate.getAsActualType(key);
100         }
101 
102         public void setBoolean(String key, boolean value) throws PropertyException
103         {
104             delegate.setBoolean(key, value);
105             recache();
106         }
107 
108         public boolean getBoolean(String key) throws PropertyException
109         {
110             return delegate.getBoolean(key);
111         }
112 
113         public void setData(String key, byte[] value) throws PropertyException
114         {
115             delegate.setData(key, value);
116             recache();
117         }
118 
119         public byte[] getData(String key) throws PropertyException
120         {
121             return delegate.getData(key);
122         }
123 
124         public void setDate(String key, Date value) throws PropertyException
125         {
126             delegate.setDate(key, value);
127             recache();
128         }
129 
130         public Date getDate(String key) throws PropertyException
131         {
132             return delegate.getDate(key);
133         }
134 
135         public void setDouble(String key, double value) throws PropertyException
136         {
137             delegate.setDouble(key, value);
138             recache();
139         }
140 
141         public double getDouble(String key) throws PropertyException
142         {
143             return delegate.getDouble(key);
144         }
145 
146         public void setInt(String key, int value) throws PropertyException
147         {
148             delegate.setInt(key, value);
149             recache();
150         }
151 
152         public int getInt(String key) throws PropertyException
153         {
154             return delegate.getInt(key);
155         }
156 
157         public Collection getKeys() throws PropertyException
158         {
159             return delegate.getKeys();
160         }
161 
162         public Collection getKeys(int type) throws PropertyException
163         {
164             return delegate.getKeys(type);
165         }
166 
167         public Collection getKeys(String prefix) throws PropertyException
168         {
169             return delegate.getKeys(prefix);
170         }
171 
172         public Collection getKeys(String prefix, int type) throws PropertyException
173         {
174             return delegate.getKeys(prefix, type);
175         }
176 
177         public void setLong(String key, long value) throws PropertyException
178         {
179             delegate.setLong(key, value);
180             recache();
181         }
182 
183         public long getLong(String key) throws PropertyException
184         {
185             return delegate.getLong(key);
186         }
187 
188         public void setObject(String key, Object value) throws PropertyException
189         {
190             delegate.setObject(key, value);
191             recache();
192         }
193 
194         public Object getObject(String key) throws PropertyException
195         {
196             return delegate.getObject(key);
197         }
198 
199         public void setProperties(String key, Properties value) throws PropertyException
200         {
201             delegate.setProperties(key, value);
202             recache();
203         }
204 
205         public Properties getProperties(String key) throws PropertyException
206         {
207             return delegate.getProperties(key);
208         }
209 
210         public void setSchema(PropertySetSchema schema) throws PropertyException
211         {
212             delegate.setSchema(schema);
213             recache();
214         }
215 
216         public PropertySetSchema getSchema() throws PropertyException
217         {
218             return delegate.getSchema();
219         }
220 
221         public boolean isSettable(String property)
222         {
223             return delegate.isSettable(property);
224         }
225 
226         public void setString(String key, String value) throws PropertyException
227         {
228             delegate.setString(key, value);
229             recache();
230         }
231 
232         public String getString(String key) throws PropertyException
233         {
234             return delegate.getString(key);
235         }
236 
237         public void setText(String key, String value) throws PropertyException
238         {
239             delegate.setText(key, value);
240             recache();
241         }
242 
243         public String getText(String key) throws PropertyException
244         {
245             return delegate.getText(key);
246         }
247 
248         public int getType(String key) throws PropertyException
249         {
250             return delegate.getType(key);
251         }
252 
253         public void setXML(String key, Document value) throws PropertyException
254         {
255             delegate.setXML(key, value);
256             recache();
257         }
258 
259         public Document getXML(String key) throws PropertyException
260         {
261             return delegate.getXML(key);
262         }
263 
264         public boolean exists(String key) throws PropertyException
265         {
266             return delegate.exists(key);
267         }
268 
269         public void remove(String key) throws PropertyException
270         {
271             delegate.remove(key);
272             recache();
273         }
274 
275         public boolean supportsType(int type)
276         {
277             return delegate.supportsType(type);
278         }
279 
280         public boolean supportsTypes()
281         {
282             return delegate.supportsTypes();
283         }
284 
285         private void recache()
286         {
287             getCache().put(cacheKey, delegate);
288         }
289     }
290 }
291