View Javadoc

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.ui.client.service.SearchRpcService;
26  import org.kuali.student.r1.common.assembly.transform.IdTranslatorFilter;
27  import org.kuali.student.r2.common.exceptions.MissingParameterException;
28  import org.kuali.student.r2.core.search.dto.*;
29  import org.kuali.student.r2.core.search.service.SearchService;
30  import org.kuali.student.r2.common.util.ContextUtils;
31  import org.springframework.beans.factory.InitializingBean;
32  
33  import com.google.gwt.user.server.rpc.RemoteServiceServlet;
34  
35  public class SearchDispatchRpcGwtServlet extends RemoteServiceServlet implements SearchRpcService, InitializingBean {
36  
37      private static final long serialVersionUID = 1L;
38  
39      private IdTranslatorFilter idTranslatorFilter;
40  
41      private SearchService searchDispatcher;
42  
43      protected boolean cachingEnabled = false;
44  	protected int searchCacheMaxSize = 20;
45  	protected int searchCacheMaxAgeSeconds = 90;
46  	protected Map<String,MaxAgeSoftReference<SearchResultInfo>> searchCache;
47  	
48      
49      public SearchDispatchRpcGwtServlet() {
50          super();
51      }
52  
53      /**
54       * Delegates to the service responsible for the given search type key
55       *
56       * @param searchRequest
57       * @return The searchResult from the delegated search or null
58       * @throws MissingParameterException
59       */
60      @Override
61      public SearchResultInfo search(SearchRequestInfo searchRequest) {
62          try
63          {
64              SearchResultInfo searchResult = searchDispatcher.search(searchRequest, ContextUtils.getContextInfo());
65              List<SearchParamInfo> params = searchRequest.getParams();
66              if (params != null && params.size() > 0) {
67                  SearchParamInfo firstParam = params.get(0);
68                  if (firstParam.getKey().equals("lu.queryParam.cluVersionIndId")) {//FIXME can this special case be handled after this call?
69                      doIdTranslation(searchResult);
70                  }
71              }
72              return searchResult;
73          } catch (Exception ex) {
74              // Log exception 
75              ex.printStackTrace();
76              throw new RuntimeException(ex);
77          }
78      }
79  
80      @Override
81      public SearchResultInfo cachingSearch(SearchRequestInfo searchRequest) {
82          try
83          {
84              String cacheKey = searchRequest.toString();
85              if (cachingEnabled) {
86  
87                  //Get From Cache
88                  MaxAgeSoftReference<SearchResultInfo> ref = searchCache.get(cacheKey);
89                  if (ref != null) {
90                      SearchResultInfo cachedSearchResult = ref.get();
91                      if (cachedSearchResult != null) {
92                          return cachedSearchResult;
93                      }
94                  }
95              }
96  
97              //Perform the actual Search
98              SearchResultInfo searchResult = search(searchRequest);
99  
100             if (cachingEnabled) {
101                 //Store to cache
102                 searchCache
103                         .put(cacheKey, new MaxAgeSoftReference<SearchResultInfo>(searchCacheMaxAgeSeconds, searchResult));
104             }
105 
106             return searchResult;
107         } catch (Exception ex) {
108             // Log exception 
109             ex.printStackTrace();
110             throw new RuntimeException(ex);
111         }
112     }
113 
114     private void doIdTranslation(SearchResultInfo searchResult) {
115         for (SearchResultRowInfo searchResultRow : searchResult.getRows()) {
116             for (SearchResultCellInfo searchResultCell : searchResultRow.getCells()) {
117                 String value = searchResultCell.getValue();
118                 if (value != null && value.startsWith("kuali.atp")) {
119                     String newValue = idTranslatorFilter.getTranslationForAtp(value, ContextUtils.getContextInfo());
120                     if (newValue != null) {
121                         searchResultCell.setValue(newValue);
122                     }
123                 }
124             }
125         }
126     }
127 
128 	@Override
129 	public void afterPropertiesSet() throws Exception {
130 		if(cachingEnabled){
131 			searchCache = Collections.synchronizedMap( new MaxSizeMap<String,MaxAgeSoftReference<SearchResultInfo>>( searchCacheMaxSize ) );
132 		}
133 	}
134     
135     public void setSearchDispatcher(SearchService searchDispatcher) {
136         this.searchDispatcher = searchDispatcher;
137     }
138 
139     public void setIdTranslatorFilter(IdTranslatorFilter idTranslatorFilter) {
140         this.idTranslatorFilter = idTranslatorFilter;
141     }
142 
143 	public void setCachingEnabled(boolean cachingEnabled) {
144 		this.cachingEnabled = cachingEnabled;
145 	}
146 
147 	public void setSearchCacheMaxSize(int searchCacheMaxSize) {
148 		this.searchCacheMaxSize = searchCacheMaxSize;
149 	}
150 
151 	public void setSearchCacheMaxAgeSeconds(int searchCacheMaxAgeSeconds) {
152 		this.searchCacheMaxAgeSeconds = searchCacheMaxAgeSeconds;
153 	}
154 	
155 	
156 	//Added as inner classes to avoid huge dependency on rice-impl
157 	/**
158 	 * An extension to SoftReference that stores an expiration time for the 
159 	 * value stored in the SoftReference. If no expiration time is passed in
160 	 * the value will never be cached.  
161 	 */
162 	public class MaxAgeSoftReference<T> extends SoftReference<T> {
163 		
164 		private long expires;
165 
166 		public MaxAgeSoftReference(long expires, T referent) {
167 			super(referent);
168 			this.expires = System.currentTimeMillis() + expires * 1000;
169 		}
170 		
171 		public boolean isValid() {
172 			return System.currentTimeMillis() < expires;
173 		}
174 		
175 		public T get() {			
176 			return isValid() ? super.get() : null;
177 		}		
178 		
179 	}
180 	/**
181 	 * This class acts like an LRU cache, automatically purging contents when it gets above a certain size. 
182 	 * 
183 	 * @author Kuali Rice Team (rice.collab@kuali.org)
184 	 */
185 	public class MaxSizeMap<K,V> extends LinkedHashMap<K,V> {
186 		private static final long serialVersionUID = -5354227348838839919L;
187 
188 		private int maxSize;
189 		
190 		/**
191 		 * @param maxSize
192 		 */
193 		public MaxSizeMap( int maxSize  ) {
194 			this( maxSize, false );
195 		}
196 		/**
197 		 * @param maxSize
198 		 * @param accessOrder Whether to sort in the order accessed rather than the order inserted.
199 		 */
200 		public MaxSizeMap( int maxSize, boolean accessOrder ) {
201 			super( maxSize / 2, 0.75f, accessOrder );
202 			this.maxSize = maxSize;
203 		}
204 		
205 		/**
206 		 * @see java.util.LinkedHashMap#removeEldestEntry(java.util.Map.Entry)
207 		 */
208 		@Override
209 		protected boolean removeEldestEntry(Entry<K,V> eldest) {
210 			return size() > maxSize;
211 		}
212 		
213 	}
214 }