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.krad.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.KewApiConstants;
22  import org.kuali.rice.kew.api.WorkflowDocument;
23  import org.kuali.rice.krad.UserSession;
24  import org.kuali.rice.krad.bo.SessionDocument;
25  import org.kuali.rice.krad.dao.SessionDocumentDao;
26  import org.kuali.rice.krad.datadictionary.DocumentEntry;
27  import org.kuali.rice.krad.service.BusinessObjectService;
28  import org.kuali.rice.krad.service.DataDictionaryService;
29  import org.kuali.rice.krad.service.KRADServiceLocatorWeb;
30  import org.kuali.rice.krad.service.SessionDocumentService;
31  import org.kuali.rice.krad.web.form.DocumentFormBase;
32  import org.springframework.transaction.annotation.Transactional;
33  
34  import java.io.ByteArrayInputStream;
35  import java.io.ByteArrayOutputStream;
36  import java.io.ObjectInputStream;
37  import java.io.ObjectOutputStream;
38  import java.sql.Timestamp;
39  import java.util.HashMap;
40  import java.util.Map;
41  
42  /**
43   * Implementation of <code>SessionDocumentService</code> that persists the document form
44   * contents to the underlying database
45   *
46   * @author Kuali Rice Team (rice.collab@kuali.org)
47   */
48  @Transactional
49  public class SessionDocumentServiceImpl implements SessionDocumentService {
50      private static org.apache.log4j.Logger LOG = org.apache.log4j.Logger.getLogger(SessionDocumentServiceImpl.class);
51  
52      protected static final String IP_ADDRESS = "ipAddress";
53      protected static final String PRINCIPAL_ID = "principalId";
54      protected static final String DOCUMENT_NUMBER = "documentNumber";
55      protected static final String SESSION_ID = "sessionId";
56  
57      private EncryptionService encryptionService;
58  
59      private BusinessObjectService businessObjectService;
60      private DataDictionaryService dataDictionaryService;
61      private SessionDocumentDao sessionDocumentDao;
62  
63      @Override
64      public DocumentFormBase getDocumentForm(String documentNumber, String docFormKey, UserSession userSession,
65              String ipAddress) {
66          DocumentFormBase documentForm = null;
67  
68          LOG.debug("getDocumentForm DocumentFormBase from db");
69          try {
70              // re-create the DocumentFormBase object
71              documentForm = (DocumentFormBase) retrieveDocumentForm(userSession, docFormKey, documentNumber, ipAddress);
72  
73              //re-store workFlowDocument into session
74              WorkflowDocument workflowDocument =
75                      documentForm.getDocument().getDocumentHeader().getWorkflowDocument();
76              addDocumentToUserSession(userSession, workflowDocument);
77          } catch (Exception e) {
78              LOG.error("getDocumentForm failed for SessId/DocNum/PrinId/IP:" + userSession.getKualiSessionId() + "/" +
79                      documentNumber + "/" + userSession.getPrincipalId() + "/" + ipAddress, e);
80          }
81  
82          return documentForm;
83      }
84  
85      protected Object retrieveDocumentForm(UserSession userSession, String sessionId, String documentNumber,
86              String ipAddress) throws Exception {
87          HashMap<String, String> primaryKeys = new HashMap<String, String>(4);
88          primaryKeys.put(SESSION_ID, sessionId);
89          if (documentNumber != null) {
90              primaryKeys.put(DOCUMENT_NUMBER, documentNumber);
91          }
92          primaryKeys.put(PRINCIPAL_ID, userSession.getPrincipalId());
93          primaryKeys.put(IP_ADDRESS, ipAddress);
94  
95          SessionDocument sessionDoc = getBusinessObjectService().findByPrimaryKey(SessionDocument.class, primaryKeys);
96          if (sessionDoc != null) {
97              byte[] formAsBytes = sessionDoc.getSerializedDocumentForm();
98              if (sessionDoc.isEncrypted()) {
99                  formAsBytes = getEncryptionService().decryptBytes(formAsBytes);
100             }
101             ByteArrayInputStream baip = new ByteArrayInputStream(formAsBytes);
102             ObjectInputStream ois = new ObjectInputStream(baip);
103 
104             return ois.readObject();
105         }
106 
107         return null;
108     }
109 
110     @Override
111     public WorkflowDocument getDocumentFromSession(UserSession userSession, String docId) {
112         @SuppressWarnings("unchecked") Map<String, WorkflowDocument> workflowDocMap =
113                 (Map<String, WorkflowDocument>) userSession
114                         .retrieveObject(KewApiConstants.WORKFLOW_DOCUMENT_MAP_ATTR_NAME);
115 
116         if (workflowDocMap == null) {
117             workflowDocMap = new HashMap<String, WorkflowDocument>();
118             userSession.addObject(KewApiConstants.WORKFLOW_DOCUMENT_MAP_ATTR_NAME, workflowDocMap);
119             return null;
120         }
121         return workflowDocMap.get(docId);
122     }
123 
124     /**
125      * @see org.kuali.rice.krad.service.SessionDocumentService#addDocumentToUserSession(org.kuali.rice.krad.UserSession,
126      *      org.kuali.rice.krad.workflow.service.KualiWorkflowDocument)
127      */
128     @Override
129     public void addDocumentToUserSession(UserSession userSession, WorkflowDocument document) {
130         @SuppressWarnings("unchecked") Map<String, WorkflowDocument> workflowDocMap =
131                 (Map<String, WorkflowDocument>) userSession
132                         .retrieveObject(KewApiConstants.WORKFLOW_DOCUMENT_MAP_ATTR_NAME);
133         if (workflowDocMap == null) {
134             workflowDocMap = new HashMap<String, WorkflowDocument>();
135         }
136         workflowDocMap.put(document.getDocumentId(), document);
137         userSession.addObject(KewApiConstants.WORKFLOW_DOCUMENT_MAP_ATTR_NAME, workflowDocMap);
138     }
139 
140     /**
141      * @see org.kuali.rice.krad.service.SessionDocumentService#purgeDocumentForm(String
142      *      documentNumber, String docFormKey, UserSession userSession)
143      */
144     @Override
145     public void purgeDocumentForm(String documentNumber, String docFormKey, UserSession userSession, String ipAddress) {
146         synchronized (userSession) {
147 
148             LOG.debug("purge document form from session");
149             userSession.removeObject(docFormKey);
150             try {
151                 LOG.debug("purge document form from database");
152                 HashMap<String, String> primaryKeys = new HashMap<String, String>(4);
153                 primaryKeys.put(SESSION_ID, userSession.getKualiSessionId());
154                 primaryKeys.put(DOCUMENT_NUMBER, documentNumber);
155                 primaryKeys.put(PRINCIPAL_ID, userSession.getPrincipalId());
156                 primaryKeys.put(IP_ADDRESS, ipAddress);
157                 getBusinessObjectService().deleteMatching(SessionDocument.class, primaryKeys);
158             } catch (Exception e) {
159                 LOG.error("purgeDocumentForm failed for SessId/DocNum/PrinId/IP:" + userSession.getKualiSessionId() +
160                         "/" + documentNumber + "/" + userSession.getPrincipalId() + "/" + ipAddress, e);
161             }
162         }
163     }
164 
165     @Override
166     public void setDocumentForm(DocumentFormBase form, UserSession userSession, String ipAddress) {
167         synchronized (userSession) {
168             //formKey was set in KualiDocumentActionBase execute method
169             String formKey = form.getFormKey();
170             String key = userSession.getKualiSessionId() + "-" + formKey;
171 
172             String documentNumber = form.getDocument().getDocumentNumber();
173             if (StringUtils.isNotBlank(formKey)) {
174                 //FIXME: Currently using formKey for sessionId
175                 persistDocumentForm(form, userSession, ipAddress, formKey, documentNumber);
176             } else {
177                 LOG.warn("documentNumber is null on form's document: " + form);
178             }
179         }
180     }
181 
182     protected void persistDocumentForm(DocumentFormBase form, UserSession userSession, String ipAddress,
183             String sessionId, String documentNumber) {
184         try {
185             LOG.debug("set Document Form into database");
186 
187             Timestamp currentTime = new Timestamp(System.currentTimeMillis());
188             ByteArrayOutputStream baos = new ByteArrayOutputStream();
189             ObjectOutputStream oos = new ObjectOutputStream(baos);
190             oos.writeObject(form);
191 
192             // serialize the DocumentFormBase object into a byte array
193             byte[] formAsBytes = baos.toByteArray();
194             boolean encryptContent = false;
195             DocumentEntry documentEntry =
196                     getDataDictionaryService().getDataDictionary().getDocumentEntry(form.getDocTypeName());
197             if (documentEntry != null) {
198                 encryptContent = documentEntry.isEncryptDocumentDataInPersistentSessionStorage();
199             }
200 
201             if (encryptContent) {
202                 formAsBytes = getEncryptionService().encryptBytes(formAsBytes);
203             }
204 
205             // check if a record is already there in the database
206             // this may only happen under jMeter testing, but there is no way to be sure
207             HashMap<String, String> primaryKeys = new HashMap<String, String>(4);
208             primaryKeys.put(SESSION_ID, sessionId);
209             primaryKeys.put(DOCUMENT_NUMBER, documentNumber);
210             primaryKeys.put(PRINCIPAL_ID, userSession.getPrincipalId());
211             primaryKeys.put(IP_ADDRESS, ipAddress);
212 
213             SessionDocument sessionDocument =
214                     getBusinessObjectService().findByPrimaryKey(SessionDocument.class, primaryKeys);
215             if (sessionDocument == null) {
216                 sessionDocument = new SessionDocument();
217                 sessionDocument.setSessionId(sessionId);
218                 sessionDocument.setDocumentNumber(documentNumber);
219                 sessionDocument.setPrincipalId(userSession.getPrincipalId());
220                 sessionDocument.setIpAddress(ipAddress);
221             }
222             sessionDocument.setSerializedDocumentForm(formAsBytes);
223             sessionDocument.setEncrypted(encryptContent);
224             sessionDocument.setLastUpdatedDate(currentTime);
225 
226             businessObjectService.save(sessionDocument);
227         } catch (Exception e) {
228             final String className = form != null ? form.getClass().getName() : "null";
229             LOG.error("setDocumentForm failed for SessId/DocNum/PrinId/IP/class:" + userSession.getKualiSessionId() +
230                     "/" + documentNumber + "/" + userSession.getPrincipalId() + "/" + ipAddress + "/" + className, e);
231         }
232     }
233 
234     /**
235      * @see org.kuali.rice.krad.service.SessionDocumentService#purgeAllSessionDocuments(java.sql.Timestamp)
236      */
237     @Override
238     public void purgeAllSessionDocuments(Timestamp expirationDate) {
239         sessionDocumentDao.purgeAllSessionDocuments(expirationDate);
240     }
241 
242     protected SessionDocumentDao getSessionDocumentDao() {
243         return this.sessionDocumentDao;
244     }
245 
246     public void setSessionDocumentDao(SessionDocumentDao sessionDocumentDao) {
247         this.sessionDocumentDao = sessionDocumentDao;
248     }
249 
250     protected BusinessObjectService getBusinessObjectService() {
251         return this.businessObjectService;
252     }
253 
254     public void setBusinessObjectService(BusinessObjectService businessObjectService) {
255         this.businessObjectService = businessObjectService;
256     }
257 
258     protected EncryptionService getEncryptionService() {
259         if (encryptionService == null) {
260             encryptionService = CoreApiServiceLocator.getEncryptionService();
261         }
262         return encryptionService;
263     }
264 
265     protected DataDictionaryService getDataDictionaryService() {
266         if (dataDictionaryService == null) {
267             dataDictionaryService = KRADServiceLocatorWeb.getDataDictionaryService();
268         }
269         return dataDictionaryService;
270     }
271 }