View Javadoc

1   /**
2    * Copyright 2005-2014 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.kns.service.impl;
17  
18  import org.apache.commons.collections.map.LRUMap;
19  import org.apache.commons.lang.StringUtils;
20  import org.apache.log4j.Logger;
21  import org.kuali.rice.core.api.CoreApiServiceLocator;
22  import org.kuali.rice.core.api.encryption.EncryptionService;
23  import org.kuali.rice.kew.api.WorkflowDocument;
24  import org.kuali.rice.kns.document.authorization.DocumentAuthorizerBase;
25  import org.kuali.rice.kns.service.SessionDocumentService;
26  import org.kuali.rice.kns.web.struts.form.KualiDocumentFormBase;
27  import org.kuali.rice.krad.UserSession;
28  import org.kuali.rice.krad.UserSessionUtils;
29  import org.kuali.rice.krad.bo.SessionDocument;
30  import org.kuali.rice.krad.dao.SessionDocumentDao;
31  import org.kuali.rice.krad.datadictionary.DocumentEntry;
32  import org.kuali.rice.krad.service.BusinessObjectService;
33  import org.kuali.rice.krad.service.DataDictionaryService;
34  import org.kuali.rice.krad.service.KRADServiceLocatorWeb;
35  import org.kuali.rice.krad.util.KRADConstants;
36  import org.springframework.beans.factory.InitializingBean;
37  import org.springframework.transaction.annotation.Transactional;
38  
39  import java.io.ByteArrayInputStream;
40  import java.io.ByteArrayOutputStream;
41  import java.io.ObjectInputStream;
42  import java.io.ObjectOutputStream;
43  import java.sql.Timestamp;
44  import java.util.Collections;
45  import java.util.HashMap;
46  import java.util.Map;
47  
48  /**
49   * Implementation of <code>SessionDocumentService</code> that persists the document form
50   * contents to the underlying database
51   *
52   * @author Kuali Rice Team (rice.collab@kuali.org)
53   */
54  @Deprecated
55  @Transactional
56  public class SessionDocumentServiceImpl implements SessionDocumentService, InitializingBean {
57      private static org.apache.log4j.Logger LOG = org.apache.log4j.Logger.getLogger(SessionDocumentServiceImpl.class);
58  
59      protected static final String IP_ADDRESS = "ipAddress";
60      protected static final String PRINCIPAL_ID = "principalId";
61      protected static final String DOCUMENT_NUMBER = "documentNumber";
62      protected static final String SESSION_ID = "sessionId";
63  
64      private Map<String, CachedObject> cachedObjects;
65      private EncryptionService encryptionService;
66      private int maxCacheSize;
67  
68      private BusinessObjectService businessObjectService;
69      private DataDictionaryService dataDictionaryService;
70      private SessionDocumentDao sessionDocumentDao;
71  
72      private static class CachedObject {
73          private UserSession userSession;
74          private String formKey;
75  
76          CachedObject(UserSession userSession, String formKey) {
77              this.userSession = userSession;
78              this.formKey = formKey;
79          }
80  
81          @Override
82          public String toString() {
83              return "CachedObject: principalId=" + userSession.getPrincipalId() + " / objectWithFormKey=" +
84                      userSession.retrieveObject(formKey);
85          }
86  
87          public UserSession getUserSession() {
88              return this.userSession;
89          }
90  
91          public String getFormKey() {
92              return this.formKey;
93          }
94      }
95  
96      /**
97       * Override LRUMap removeEntity method
98       *
99       *
100      */
101     private static class KualiLRUMap extends LRUMap {
102 
103         /** Serialization version */
104         private static final long serialVersionUID = 1L;
105 
106         private KualiLRUMap() {
107             super();
108         }
109 
110         private KualiLRUMap(int maxSize) {
111             super(maxSize);
112         }
113 
114         @Override
115         protected void removeEntry(HashEntry entry, int hashIndex, HashEntry previous) {
116 
117             // It is for session document cache enhancement.
118             // To control the size of cache. When the LRUMap reach the maxsize.
119             // It will remove session document entries from the in-memory user
120             // session objects.
121             try {
122                 CachedObject cachedObject
123                         = (CachedObject)this.entryValue(entry);
124                 cachedObject.getUserSession().removeObject(cachedObject.getFormKey());
125             } catch (Exception ex) {
126                 Logger.getLogger(getClass()).warn( "Problem purging old entry from the user session when removing from the map: ", ex);
127             }
128 
129             super.removeEntry(entry, hashIndex, previous);
130         }
131 
132     }
133 
134     @Override
135     @SuppressWarnings("unchecked")
136     public void afterPropertiesSet() throws Exception {
137         cachedObjects = Collections.synchronizedMap(new KualiLRUMap(maxCacheSize));
138     }
139 
140 
141     @Override
142     public KualiDocumentFormBase getDocumentForm(String documentNumber, String docFormKey, UserSession userSession,
143             String ipAddress) {
144         KualiDocumentFormBase documentForm = null;
145 
146         LOG.debug("getDocumentForm KualiDocumentFormBase from db");
147         try {
148             // re-create the KualiDocumentFormBase object
149             documentForm = (KualiDocumentFormBase) retrieveDocumentForm(userSession, userSession.getKualiSessionId(),
150                     documentNumber, ipAddress);
151 
152             //re-store workFlowDocument into session
153             if (!(StringUtils.equals((String)userSession.retrieveObject(DocumentAuthorizerBase.USER_SESSION_METHOD_TO_CALL_OBJECT_KEY),
154                     KRADConstants.TableRenderConstants.SORT_METHOD) ||
155                   StringUtils.equals((String)userSession.retrieveObject(DocumentAuthorizerBase.USER_SESSION_METHOD_TO_CALL_OBJECT_KEY),
156                     KRADConstants.PARAM_MAINTENANCE_VIEW_MODE_INQUIRY))) {
157                         WorkflowDocument workflowDocument =
158                             documentForm.getDocument().getDocumentHeader().getWorkflowDocument();
159                 UserSessionUtils.addWorkflowDocument(userSession, workflowDocument);
160             }
161         } catch (Exception e) {
162             LOG.error("getDocumentForm failed for SessId/DocNum/PrinId/IP:" + userSession.getKualiSessionId() + "/" +
163                     documentNumber + "/" + userSession.getPrincipalId() + "/" + ipAddress, e);
164         }
165 
166         return documentForm;
167     }
168 
169     protected Object retrieveDocumentForm(UserSession userSession, String sessionId, String documentNumber,
170             String ipAddress) throws Exception {
171         HashMap<String, String> primaryKeys = new HashMap<String, String>(4);
172         primaryKeys.put(SESSION_ID, sessionId);
173         if (documentNumber != null) {
174             primaryKeys.put(DOCUMENT_NUMBER, documentNumber);
175         }
176         primaryKeys.put(PRINCIPAL_ID, userSession.getPrincipalId());
177         primaryKeys.put(IP_ADDRESS, ipAddress);
178 
179         SessionDocument sessionDoc = getBusinessObjectService().findByPrimaryKey(SessionDocument.class, primaryKeys);
180         if (sessionDoc != null) {
181             byte[] formAsBytes = sessionDoc.getSerializedDocumentForm();
182             if (sessionDoc.isEncrypted()) {
183                 formAsBytes = getEncryptionService().decryptBytes(formAsBytes);
184             }
185             ByteArrayInputStream baip = new ByteArrayInputStream(formAsBytes);
186             ObjectInputStream ois = new ObjectInputStream(baip);
187 
188             return ois.readObject();
189         }
190 
191         return null;
192     }
193 
194     @Override
195     public WorkflowDocument getDocumentFromSession(UserSession userSession, String docId) {
196         return UserSessionUtils.getWorkflowDocument(userSession, docId);
197     }
198 
199     /**
200      * @see org.kuali.rice.krad.service.SessionDocumentService#addDocumentToUserSession(org.kuali.rice.krad.UserSession,
201      *      org.kuali.rice.kew.api.WorkflowDocument)
202      */
203     @Override
204     public void addDocumentToUserSession(UserSession userSession, WorkflowDocument document) {
205         UserSessionUtils.addWorkflowDocument(userSession, document);
206     }
207 
208     /**
209      * @see org.kuali.rice.krad.service.SessionDocumentService#purgeDocumentForm(String
210      *      documentNumber, String docFormKey, UserSession userSession)
211      */
212     @Override
213     public void purgeDocumentForm(String documentNumber, String docFormKey, UserSession userSession, String ipAddress) {
214         synchronized (userSession) {
215 
216             LOG.debug("purge document form from session");
217             userSession.removeObject(docFormKey);
218             try {
219                 LOG.debug("purge document form from database");
220                 HashMap<String, String> primaryKeys = new HashMap<String, String>(4);
221                 primaryKeys.put(SESSION_ID, userSession.getKualiSessionId());
222                 primaryKeys.put(DOCUMENT_NUMBER, documentNumber);
223                 primaryKeys.put(PRINCIPAL_ID, userSession.getPrincipalId());
224                 primaryKeys.put(IP_ADDRESS, ipAddress);
225                 getBusinessObjectService().deleteMatching(SessionDocument.class, primaryKeys);
226             } catch (Exception e) {
227                 LOG.error("purgeDocumentForm failed for SessId/DocNum/PrinId/IP:" + userSession.getKualiSessionId() +
228                         "/" + documentNumber + "/" + userSession.getPrincipalId() + "/" + ipAddress, e);
229             }
230         }
231     }
232 
233     @Override
234     public void setDocumentForm(KualiDocumentFormBase form, UserSession userSession, String ipAddress) {
235         synchronized (userSession) {
236             //formKey was set in KualiDocumentActionBase execute method
237             String formKey = form.getFormKey();
238             String key = userSession.getKualiSessionId() + "-" + formKey;
239             cachedObjects.put(key, new CachedObject(userSession, formKey));
240 
241             String documentNumber = form.getDocument().getDocumentNumber();
242 
243             if (StringUtils.isNotBlank(documentNumber)) {
244                 persistDocumentForm(form, userSession, ipAddress, userSession.getKualiSessionId(), documentNumber);
245             } else {
246                 LOG.warn("documentNumber is null on form's document: " + form);
247             }
248         }
249     }
250 
251     protected void persistDocumentForm(Object form, UserSession userSession, String ipAddress, String sessionId,
252             String documentNumber) {
253         try {
254             LOG.debug("set Document Form into database");
255             Timestamp currentTime = new Timestamp(System.currentTimeMillis());
256             ByteArrayOutputStream baos = new ByteArrayOutputStream();
257             ObjectOutputStream oos = new ObjectOutputStream(baos);
258             oos.writeObject(form);
259             // serialize the KualiDocumentFormBase object into a byte array
260             byte[] formAsBytes = baos.toByteArray();
261             boolean encryptContent = false;
262 
263             if ((form instanceof KualiDocumentFormBase) && ((KualiDocumentFormBase) form).getDocTypeName() != null) {
264                 DocumentEntry documentEntry = getDataDictionaryService().getDataDictionary()
265                         .getDocumentEntry(((KualiDocumentFormBase) form).getDocTypeName());
266                 if (documentEntry != null) {
267                     encryptContent = documentEntry.isEncryptDocumentDataInPersistentSessionStorage();
268                 }
269             }
270             if (encryptContent) {
271                 formAsBytes = getEncryptionService().encryptBytes(formAsBytes);
272             }
273 
274             // check if a record is already there in the database
275             // this may only happen under jMeter testing, but there is no way to be sure
276             HashMap<String, String> primaryKeys = new HashMap<String, String>(4);
277             primaryKeys.put(SESSION_ID, sessionId);
278             primaryKeys.put(DOCUMENT_NUMBER, documentNumber);
279             primaryKeys.put(PRINCIPAL_ID, userSession.getPrincipalId());
280             primaryKeys.put(IP_ADDRESS, ipAddress);
281 
282             SessionDocument sessionDocument =
283                     getBusinessObjectService().findByPrimaryKey(SessionDocument.class, primaryKeys);
284             if (sessionDocument == null) {
285                 sessionDocument = new SessionDocument();
286                 sessionDocument.setSessionId(sessionId);
287                 sessionDocument.setDocumentNumber(documentNumber);
288                 sessionDocument.setPrincipalId(userSession.getPrincipalId());
289                 sessionDocument.setIpAddress(ipAddress);
290             }
291             sessionDocument.setSerializedDocumentForm(formAsBytes);
292             sessionDocument.setEncrypted(encryptContent);
293             sessionDocument.setLastUpdatedDate(currentTime);
294 
295             businessObjectService.save(sessionDocument);
296         } catch (Exception e) {
297             final String className = form != null ? form.getClass().getName() : "null";
298             LOG.error("setDocumentForm failed for SessId/DocNum/PrinId/IP/class:" + userSession.getKualiSessionId() +
299                     "/" + documentNumber + "/" + userSession.getPrincipalId() + "/" + ipAddress + "/" + className, e);
300         }
301     }
302 
303     /**
304      * @see org.kuali.rice.krad.service.SessionDocumentService#purgeAllSessionDocuments(java.sql.Timestamp)
305      */
306     @Override
307     public void purgeAllSessionDocuments(Timestamp expirationDate) {
308         sessionDocumentDao.purgeAllSessionDocuments(expirationDate);
309     }
310 
311     protected SessionDocumentDao getSessionDocumentDao() {
312         return this.sessionDocumentDao;
313     }
314 
315     public void setSessionDocumentDao(SessionDocumentDao sessionDocumentDao) {
316         this.sessionDocumentDao = sessionDocumentDao;
317     }
318 
319     protected BusinessObjectService getBusinessObjectService() {
320         return this.businessObjectService;
321     }
322 
323     public void setBusinessObjectService(BusinessObjectService businessObjectService) {
324         this.businessObjectService = businessObjectService;
325     }
326 
327     public int getMaxCacheSize() {
328         return maxCacheSize;
329     }
330 
331     public void setMaxCacheSize(int maxCacheSize) {
332         this.maxCacheSize = maxCacheSize;
333     }
334 
335     protected EncryptionService getEncryptionService() {
336         if (encryptionService == null) {
337             encryptionService = CoreApiServiceLocator.getEncryptionService();
338         }
339         return encryptionService;
340     }
341 
342     protected DataDictionaryService getDataDictionaryService() {
343         if (dataDictionaryService == null) {
344             dataDictionaryService = KRADServiceLocatorWeb.getDataDictionaryService();
345         }
346         return dataDictionaryService;
347     }
348 }