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