Coverage Report - org.kuali.student.common.ui.server.gwt.SearchDispatchRpcGwtServlet
 
Classes in this File Line Coverage Branch Coverage Complexity
SearchDispatchRpcGwtServlet
0%
0/45
0%
0/26
1.938
SearchDispatchRpcGwtServlet$MaxAgeSoftReference
0%
0/6
0%
0/4
1.938
SearchDispatchRpcGwtServlet$MaxSizeMap
0%
0/7
0%
0/2
1.938
 
 1  
 /**
 2  
  * Copyright 2010 The Kuali Foundation Licensed under the
 3  
  * Educational Community License, Version 2.0 (the "License"); you may
 4  
  * not use this file except in compliance with the License. You may
 5  
  * obtain a copy of the License at
 6  
  *
 7  
  * http://www.osedu.org/licenses/ECL-2.0
 8  
  *
 9  
  * Unless required by applicable law or agreed to in writing,
 10  
  * software distributed under the License is distributed on an "AS IS"
 11  
  * BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
 12  
  * or implied. See the License for the specific language governing
 13  
  * permissions and limitations under the License.
 14  
  */
 15  
 
 16  
 package org.kuali.student.common.ui.server.gwt;
 17  
 
 18  
 import java.lang.ref.SoftReference;
 19  
 import java.util.Collections;
 20  
 import java.util.LinkedHashMap;
 21  
 import java.util.List;
 22  
 import java.util.Map;
 23  
 import java.util.Map.Entry;
 24  
 
 25  
 import org.kuali.student.common.assembly.transform.IdTranslatorFilter;
 26  
 import org.kuali.student.common.exceptions.MissingParameterException;
 27  
 import org.kuali.student.common.search.dto.SearchParam;
 28  
 import org.kuali.student.common.search.dto.SearchRequest;
 29  
 import org.kuali.student.common.search.dto.SearchResult;
 30  
 import org.kuali.student.common.search.dto.SearchResultCell;
 31  
 import org.kuali.student.common.search.dto.SearchResultRow;
 32  
 import org.kuali.student.common.search.service.SearchDispatcher;
 33  
 import org.kuali.student.common.ui.client.service.SearchRpcService;
 34  
 import org.springframework.beans.factory.InitializingBean;
 35  
 
 36  
 import com.google.gwt.user.server.rpc.RemoteServiceServlet;
 37  
 
 38  
 public class SearchDispatchRpcGwtServlet extends RemoteServiceServlet implements SearchRpcService, InitializingBean {
 39  
 
 40  
     private static final long serialVersionUID = 1L;
 41  
 
 42  
     private IdTranslatorFilter idTranslatorFilter;
 43  
 
 44  
     private SearchDispatcher searchDispatcher;
 45  
 
 46  0
     protected boolean cachingEnabled = false;
 47  0
         protected int searchCacheMaxSize = 20;
 48  0
         protected int searchCacheMaxAgeSeconds = 90;
 49  
         protected Map<String,MaxAgeSoftReference<SearchResult>> searchCache;
 50  
         
 51  
     
 52  
     public SearchDispatchRpcGwtServlet() {
 53  0
         super();
 54  0
     }
 55  
 
 56  
     /**
 57  
      * Delegates to the service responsible for the given search type key
 58  
      *
 59  
      * @param searchRequest
 60  
      * @return The searchResult from the delegated search or null
 61  
      * @throws MissingParameterException
 62  
      */
 63  
     @Override
 64  
     public SearchResult search(SearchRequest searchRequest) {
 65  0
         SearchResult searchResult = searchDispatcher.dispatchSearch(searchRequest);
 66  0
         List<SearchParam> params  = searchRequest.getParams();
 67  0
         if(params != null && params.size() > 0){
 68  0
             SearchParam firstParam = params.get(0);
 69  0
             if(firstParam.getKey().equals("lu.queryParam.cluVersionIndId")){//FIXME can this special case be handled after this call?
 70  0
                 doIdTranslation(searchResult);
 71  
             }
 72  
         }
 73  0
         return searchResult;
 74  
     }
 75  
 
 76  
     @Override
 77  
     public SearchResult cachingSearch(SearchRequest searchRequest) {
 78  0
             String cacheKey = searchRequest.toString();
 79  0
             if(cachingEnabled){
 80  
                     
 81  
                     //Get From Cache
 82  0
                     MaxAgeSoftReference<SearchResult> ref = searchCache.get(cacheKey);
 83  0
                     if ( ref != null ) {
 84  0
                             SearchResult cachedSearchResult = ref.get();
 85  0
                             if(cachedSearchResult!=null){
 86  0
                                     return cachedSearchResult;
 87  
                             }
 88  
                     }
 89  
                 }
 90  
 
 91  
             //Perform the actual Search
 92  0
             SearchResult searchResult = search(searchRequest);
 93  
         
 94  0
             if(cachingEnabled){
 95  
                     //Store to cache
 96  0
                     searchCache.put(cacheKey, new MaxAgeSoftReference<SearchResult>( searchCacheMaxAgeSeconds, searchResult) );
 97  
             }
 98  
             
 99  0
         return searchResult;
 100  
     }
 101  
 
 102  
     private void doIdTranslation(SearchResult searchResult) {
 103  0
         for (SearchResultRow searchResultRow : searchResult.getRows()) {
 104  0
             for (SearchResultCell searchResultCell : searchResultRow.getCells()) {
 105  0
                 String value = searchResultCell.getValue();
 106  0
                 if (value != null && value.startsWith("kuali.atp")) {
 107  0
                     String newValue = idTranslatorFilter.getTranslationForAtp(value);
 108  0
                     if (newValue != null) {
 109  0
                         searchResultCell.setValue(newValue);
 110  
                     }
 111  
                 }
 112  0
             }
 113  
         }
 114  0
     }
 115  
 
 116  
         @Override
 117  
         public void afterPropertiesSet() throws Exception {
 118  0
                 if(cachingEnabled){
 119  0
                         searchCache = Collections.synchronizedMap( new MaxSizeMap<String,MaxAgeSoftReference<SearchResult>>( searchCacheMaxSize ) );
 120  
                 }
 121  0
         }
 122  
     
 123  
     public void setSearchDispatcher(SearchDispatcher searchDispatcher) {
 124  0
         this.searchDispatcher = searchDispatcher;
 125  0
     }
 126  
 
 127  
     public void setIdTranslatorFilter(IdTranslatorFilter idTranslatorFilter) {
 128  0
         this.idTranslatorFilter = idTranslatorFilter;
 129  0
     }
 130  
 
 131  
         public void setCachingEnabled(boolean cachingEnabled) {
 132  0
                 this.cachingEnabled = cachingEnabled;
 133  0
         }
 134  
 
 135  
         public void setSearchCacheMaxSize(int searchCacheMaxSize) {
 136  0
                 this.searchCacheMaxSize = searchCacheMaxSize;
 137  0
         }
 138  
 
 139  
         public void setSearchCacheMaxAgeSeconds(int searchCacheMaxAgeSeconds) {
 140  0
                 this.searchCacheMaxAgeSeconds = searchCacheMaxAgeSeconds;
 141  0
         }
 142  
         
 143  
         
 144  
         //Added as inner classes to avoid huge dependency on rice-impl
 145  
         /**
 146  
          * An extension to SoftReference that stores an expiration time for the 
 147  
          * value stored in the SoftReference. If no expiration time is passed in
 148  
          * the value will never be cached.  
 149  
          */
 150  
         public class MaxAgeSoftReference<T> extends SoftReference<T> {
 151  
                 
 152  
                 private long expires;
 153  
 
 154  0
                 public MaxAgeSoftReference(long expires, T referent) {
 155  0
                         super(referent);
 156  0
                         this.expires = System.currentTimeMillis() + expires * 1000;
 157  0
                 }
 158  
                 
 159  
                 public boolean isValid() {
 160  0
                         return System.currentTimeMillis() < expires;
 161  
                 }
 162  
                 
 163  
                 public T get() {                        
 164  0
                         return isValid() ? super.get() : null;
 165  
                 }                
 166  
                 
 167  
         }
 168  
         /**
 169  
          * This class acts like an LRU cache, automatically purging contents when it gets above a certain size. 
 170  
          * 
 171  
          * @author Kuali Rice Team (rice.collab@kuali.org)
 172  
          */
 173  
         public class MaxSizeMap<K,V> extends LinkedHashMap<K,V> {
 174  
                 private static final long serialVersionUID = -5354227348838839919L;
 175  
 
 176  
                 private int maxSize;
 177  
                 
 178  
                 /**
 179  
                  * @param maxSize
 180  
                  */
 181  
                 public MaxSizeMap( int maxSize  ) {
 182  0
                         this( maxSize, false );
 183  0
                 }
 184  
                 /**
 185  
                  * @param maxSize
 186  
                  * @param accessOrder Whether to sort in the order accessed rather than the order inserted.
 187  
                  */
 188  0
                 public MaxSizeMap( int maxSize, boolean accessOrder ) {
 189  0
                         super( maxSize / 2, 0.75f, accessOrder );
 190  0
                         this.maxSize = maxSize;
 191  0
                 }
 192  
                 
 193  
                 /**
 194  
                  * @see java.util.LinkedHashMap#removeEldestEntry(java.util.Map.Entry)
 195  
                  */
 196  
                 @Override
 197  
                 protected boolean removeEldestEntry(Entry<K,V> eldest) {
 198  0
                         return size() > maxSize;
 199  
                 }
 200  
                 
 201  
         }
 202  
 }