View Javadoc

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