View Javadoc
1   /**
2    * Copyright 2005-2015 The Kuali Foundation
3    *
4    * Licensed under the Educational Community License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    *
8    * http://www.opensource.org/licenses/ecl2.php
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   */
16  package org.kuali.rice.core.web.cache;
17  
18  import org.kuali.rice.core.api.cache.CacheManagerRegistry;
19  import org.kuali.rice.core.api.util.tree.Node;
20  import org.kuali.rice.core.api.util.tree.Tree;
21  import org.kuali.rice.core.impl.services.CoreImplServiceLocator;
22  import org.kuali.rice.kim.api.KimConstants;
23  import org.kuali.rice.kim.api.identity.Person;
24  import org.kuali.rice.kim.api.services.KimApiServiceLocator;
25  import org.kuali.rice.krad.util.GlobalVariables;
26  import org.kuali.rice.krad.util.KRADConstants;
27  import org.kuali.rice.krad.web.controller.UifControllerBase;
28  import org.kuali.rice.krad.web.form.UifFormBase;
29  import org.springframework.cache.CacheManager;
30  import org.springframework.stereotype.Controller;
31  import org.springframework.validation.BindingResult;
32  import org.springframework.web.bind.annotation.ModelAttribute;
33  import org.springframework.web.bind.annotation.RequestMapping;
34  import org.springframework.web.bind.annotation.RequestMethod;
35  import org.springframework.web.servlet.ModelAndView;
36  
37  import javax.servlet.http.HttpServletRequest;
38  import javax.servlet.http.HttpServletResponse;
39  import java.lang.reflect.InvocationTargetException;
40  import java.util.ArrayList;
41  import java.util.Collections;
42  import java.util.Comparator;
43  import java.util.List;
44  
45  @Controller
46  @RequestMapping(value = "/core/admin/cache")
47  public class CacheAdminController extends UifControllerBase {
48  
49      private CacheManagerRegistry registry;
50  
51      public synchronized CacheManagerRegistry getRegistry() {
52          if (registry == null) {
53              registry = CoreImplServiceLocator.getCacheManagerRegistry();
54          }
55          return registry;
56      }
57  
58      /**
59       * {@inheritDoc}
60       */
61      @Override
62      protected CacheAdminForm createInitialForm() {
63          return new CacheAdminForm();
64      }
65  
66      @Override
67  	@RequestMapping(params = "methodToCall=start")
68  	public ModelAndView start(UifFormBase form) {
69  
70          final Tree<String, String> cacheTree = new Tree<String,String>();
71  
72          final Node<String,String> root = new Node<String,String>("Root", "Root");
73          final List<CacheManager> cms = new ArrayList<CacheManager>(getRegistry().getCacheManagers());
74          Collections.sort(cms, new ByName());
75  
76          for (final CacheManager cm : cms) {
77              final String name = getRegistry().getCacheManagerName(cm);
78              final Node<String, String> cmNode = new Node<String, String>(name, name);
79              final List<String> names = new ArrayList<String>(cm.getCacheNames());
80              Collections.sort(names, String.CASE_INSENSITIVE_ORDER);
81  
82              for (final String cn : names) {
83                  String cacheSize = getCacheSize(name, cn);
84                  final Node<String, String> cNode = new Node<String, String>(cn, cn + (cacheSize != null ? " - " + cacheSize : ""));
85                  //no way to get a keySet from the cache w/o calling the nativeCache
86                  //method which is a bad idea b/c it will tie the rice codebase to
87                  //a caching implementation
88                  cmNode.addChild(cNode);
89              }
90  
91              root.addChild(cmNode);
92          }
93  
94          cacheTree.setRootElement(root);
95          ((CacheAdminForm) form).setCacheTree(cacheTree);
96  
97          return super.start(form);
98      }
99  
100 	@RequestMapping(params = "methodToCall=flush", method = RequestMethod.POST)
101 	public ModelAndView flush(UifFormBase form) {
102         Person user = GlobalVariables.getUserSession().getPerson();
103         boolean isAuthorized = KimApiServiceLocator.getPermissionService().isAuthorized(
104 						user.getPrincipalId(),
105 						KRADConstants.KUALI_RICE_SYSTEM_NAMESPACE,
106 						KRADConstants.USE_CACHE_ADMINISTRATION_SCREEN,
107 						Collections.singletonMap(KimConstants.AttributeConstants.PRINCIPAL_ID, user.getPrincipalId()));
108         if(isAuthorized){
109 
110             //FIXME: Could optimize this such that specific cache flushes don't execute if a complete CacheManager
111             //flush was requested
112             for (String name : ((CacheAdminForm) form).getFlush()) {
113                 //path == cacheManager index, cache index
114                 final List<Integer> path = path(removePrefix(name));
115                 final Tree<String, String> tree = ((CacheAdminForm) form).getCacheTree();
116                 final Integer cmIdx = path.get(0);
117                 final Node<String, String> cmNode = tree.getRootElement().getChildren().get(cmIdx);
118                 final String cmName = cmNode.getData();
119                 final CacheManager cm = getRegistry().getCacheManager(cmName);
120 
121                 if (path.size() == 1) {
122                     flushAllCaches(cm);
123                     GlobalVariables.getMessageMap().putInfoForSectionId("mainGroup_div","flush.all.cachemanager", cmName);
124                 } else {
125                     final Integer cIdx = path.get(1);
126                     final Node<String, String> cNode = cmNode.getChildren().get(cIdx);
127                     final String cName = cNode.getData();
128                     flushSpecificCache(cm, cName);
129                     GlobalVariables.getMessageMap().putInfoForSectionId("mainGroup_div",
130                             "flush.single.cachemanager", cName, cmName);
131                 }
132             }
133         }else{
134            GlobalVariables.getMessageMap().putError("flush","error.authorization.general",user.getPrincipalName(),"flush","cachemanager");
135         }
136         return super.start(form);
137     }
138 
139     private static void flushSpecificCache(CacheManager cm, String cache) {
140         for (String s : cm.getCacheNames()) {
141             if (cache.equals(s)) {
142                  cm.getCache(s).clear();
143                  return;
144             }
145         }
146     }
147 
148     private static void flushAllCaches(CacheManager cm) {
149         for (String s : cm.getCacheNames()) {
150             cm.getCache(s).clear();
151         }
152     }
153 
154     // given: 35_node_2_parent_node_0_parent_root will remove "35_"
155     private static String removePrefix(String s) {
156         final StringBuilder sbn = new StringBuilder(s);
157         sbn.delete(0, sbn.indexOf("_") + 1);
158         return sbn.toString();
159     }
160 
161     // given: node_2_parent_node_0_parent_root will return {0, 2}
162     private static List<Integer> path(String s) {
163         final String[] path = s.split("_parent_");
164         final List<Integer> pathIdx = new ArrayList<Integer>();
165         //ignore length - 2 to ignore root
166         for (int i = path.length - 2; i >= 0; i--) {
167             pathIdx.add(Integer.valueOf(path[i].substring(5)));
168         }
169         return Collections.unmodifiableList(pathIdx);
170     }
171 
172     /**
173      * If the cache manager implementation is ehcache, it will return the current size of the cache. Did this in such
174      * a way that it will fallback gracefully just in case the cache implementation being used is not ehcache.
175      */
176     private String getCacheSize(String cacheManagerName, String cacheName) {
177         Object nativeCache = getRegistry().getCacheManager(cacheManagerName).getCache(cacheName).getNativeCache();
178         try {
179             Class<?> ehcache = Class.forName("net.sf.ehcache.Cache");
180             if (ehcache.isInstance(nativeCache)) {
181                 Object intSize = ehcache.getDeclaredMethod("getSize").invoke(nativeCache);
182                 if (intSize != null) {
183                     return intSize.toString();
184                 }
185             }
186         } catch (ClassNotFoundException e) {
187             // ignore
188         } catch (NoSuchMethodException e) {
189             // ignore
190         } catch (InvocationTargetException e) {
191             // ignore
192         } catch (IllegalAccessException e) {
193             // ignore
194         }
195         return null;
196     }
197 
198     private final class ByName implements Comparator<CacheManager> {
199 
200         @Override
201         public int compare(CacheManager o1, CacheManager o2) {
202             return String.CASE_INSENSITIVE_ORDER.compare(getRegistry().getCacheManagerName(o1),
203                     getRegistry().getCacheManagerName(o2));
204         }
205     }
206 
207 }