View Javadoc

1   /**
2    * Copyright 2005-2012 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 java.util.ArrayList;
19  import java.util.Collections;
20  import java.util.Comparator;
21  import java.util.List;
22  
23  import javax.servlet.http.HttpServletRequest;
24  import javax.servlet.http.HttpServletResponse;
25  
26  import org.kuali.rice.core.api.cache.CacheManagerRegistry;
27  import org.kuali.rice.core.api.util.RiceKeyConstants;
28  import org.kuali.rice.core.api.util.tree.Node;
29  import org.kuali.rice.core.api.util.tree.Tree;
30  import org.kuali.rice.core.impl.services.CoreImplServiceLocator;
31  import org.kuali.rice.kim.api.KimConstants;
32  import org.kuali.rice.kim.api.identity.Person;
33  import org.kuali.rice.kim.api.services.KimApiServiceLocator;
34  import org.kuali.rice.krad.exception.AuthorizationException;
35  import org.kuali.rice.krad.util.GlobalVariables;
36  import org.kuali.rice.krad.util.KRADConstants;
37  import org.kuali.rice.krad.web.controller.UifControllerBase;
38  import org.kuali.rice.krad.web.form.UifFormBase;
39  import org.springframework.cache.CacheManager;
40  import org.springframework.stereotype.Controller;
41  import org.springframework.validation.BindingResult;
42  import org.springframework.web.bind.annotation.ModelAttribute;
43  import org.springframework.web.bind.annotation.RequestMapping;
44  import org.springframework.web.bind.annotation.RequestMethod;
45  import org.springframework.web.servlet.ModelAndView;
46  
47  @Controller
48  @RequestMapping(value = "/core/admin/cache")
49  public class CacheAdminController extends UifControllerBase {
50  
51      private CacheManagerRegistry registry;
52  
53      public synchronized CacheManagerRegistry getRegistry() {
54          if (registry == null) {
55              registry = CoreImplServiceLocator.getCacheManagerRegistry();
56          }
57          return registry;
58      }
59  
60      /**
61       * @see org.kuali.rice.krad.web.controller.UifControllerBase#createInitialForm(javax.servlet.http.HttpServletRequest)
62       */
63      @Override
64      protected CacheAdminForm createInitialForm(HttpServletRequest request) {
65          return new CacheAdminForm();
66      }
67  
68      @Override
69  	@RequestMapping(params = "methodToCall=start")
70  	public ModelAndView start(@ModelAttribute("KualiForm") UifFormBase form, BindingResult result,
71  			HttpServletRequest request, HttpServletResponse response) {
72  
73          final Tree<String, String> cacheTree = new Tree<String,String>();
74  
75          final Node<String,String> root = new Node<String,String>("Root", "Root");
76          final List<CacheManager> cms = new ArrayList<CacheManager>(getRegistry().getCacheManagers());
77          Collections.sort(cms, new ByName());
78  
79          for (final CacheManager cm : cms) {
80              final String name = getRegistry().getCacheManagerName(cm);
81              final Node<String, String> cmNode = new Node<String, String>(name, name);
82              final List<String> names = new ArrayList<String>(cm.getCacheNames());
83              Collections.sort(names, String.CASE_INSENSITIVE_ORDER);
84  
85              for (final String cn : names) {
86                  final Node<String, String> cNode = new Node<String, String>(cn, cn);
87                  //no way to get a keySet from the cache w/o calling the nativeCache
88                  //method which is a bad idea b/c it will tie the rice codebase to
89                  //a caching implementation
90                  cmNode.addChild(cNode);
91              }
92  
93              root.addChild(cmNode);
94          }
95  
96          cacheTree.setRootElement(root);
97          ((CacheAdminForm) form).setCacheTree(cacheTree);
98  
99          return super.start(form, result, request, response);
100     }
101 
102 	@RequestMapping(params = "methodToCall=flush", method = RequestMethod.POST)
103 	public ModelAndView flush(@ModelAttribute("KualiForm") UifFormBase form, BindingResult result,
104 			HttpServletRequest request, HttpServletResponse response) {
105         Person user = GlobalVariables.getUserSession().getPerson();
106         boolean isAuthorized = KimApiServiceLocator.getPermissionService().isAuthorized(
107 						user.getPrincipalId(),
108 						KRADConstants.KUALI_RICE_SYSTEM_NAMESPACE,
109 						KRADConstants.USE_CACHE_ADMINISTRATION_SCREEN,
110 						Collections.singletonMap(KimConstants.AttributeConstants.PRINCIPAL_ID, user.getPrincipalId()));
111         if(isAuthorized){
112 
113             //FIXME: Could optimize this such that specific cache flushes don't execute if a complete CacheManager
114             //flush was requested
115             for (String name : ((CacheAdminForm) form).getFlush()) {
116                 //path == cacheManager index, cache index
117                 final List<Integer> path = path(removePrefix(name));
118                 final Tree<String, String> tree = ((CacheAdminForm) form).getCacheTree();
119                 final Integer cmIdx = path.get(0);
120                 final Node<String, String> cmNode = tree.getRootElement().getChildren().get(cmIdx);
121                 final String cmName = cmNode.getData();
122                 final CacheManager cm = getRegistry().getCacheManager(cmName);
123 
124                 if (path.size() == 1) {
125                     flushAllCaches(cm);
126                     GlobalVariables.getMessageMap().putInfoForSectionId("mainGroup_div","flush.all.cachemanager", cmName);
127                 } else {
128                     final Integer cIdx = path.get(1);
129                     final Node<String, String> cNode = cmNode.getChildren().get(cIdx);
130                     final String cName = cNode.getData();
131                     flushSpecificCache(cm, cName);
132                     GlobalVariables.getMessageMap().putInfoForSectionId("mainGroup_div",
133                             "flush.single.cachemanager", cName, cmName);
134                 }
135             }
136         }else{
137            GlobalVariables.getMessageMap().putError("flush","error.authorization.general",user.getPrincipalName(),"flush","cachemanager");
138         }
139         return super.start(form, result, request, response);
140     }
141 
142     private static void flushSpecificCache(CacheManager cm, String cache) {
143         for (String s : cm.getCacheNames()) {
144             if (cache.equals(s)) {
145                  cm.getCache(s).clear();
146                  return;
147             }
148         }
149     }
150 
151     private static void flushAllCaches(CacheManager cm) {
152         for (String s : cm.getCacheNames()) {
153             cm.getCache(s).clear();
154         }
155     }
156 
157     // given: 35_node_2_parent_node_0_parent_root will remove "35_"
158     private static String removePrefix(String s) {
159         final StringBuilder sbn = new StringBuilder(s);
160         sbn.delete(0, sbn.indexOf("_") + 1);
161         return sbn.toString();
162     }
163 
164     // given: node_2_parent_node_0_parent_root will return {0, 2}
165     private static List<Integer> path(String s) {
166         final String[] path = s.split("_parent_");
167         final List<Integer> pathIdx = new ArrayList<Integer>();
168         //ignore length - 2 to ignore root
169         for (int i = path.length - 2; i >= 0; i--) {
170             pathIdx.add(Integer.valueOf(path[i].substring(5)));
171         }
172         return Collections.unmodifiableList(pathIdx);
173     }
174 
175     private final class ByName implements Comparator<CacheManager> {
176 
177         @Override
178         public int compare(CacheManager o1, CacheManager o2) {
179             return String.CASE_INSENSITIVE_ORDER.compare(getRegistry().getCacheManagerName(o1),
180                     getRegistry().getCacheManagerName(o2));
181         }
182     }
183 
184 }