001 /**
002 * Copyright 2005-2012 The Kuali Foundation
003 *
004 * Licensed under the Educational Community License, Version 2.0 (the "License");
005 * you may not use this file except in compliance with the License.
006 * You may obtain a copy of the License at
007 *
008 * http://www.opensource.org/licenses/ecl2.php
009 *
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS,
012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 * See the License for the specific language governing permissions and
014 * limitations under the License.
015 */
016 package org.kuali.rice.krad.service.impl;
017
018 import org.apache.commons.lang.StringUtils;
019 import org.kuali.rice.core.api.CoreApiServiceLocator;
020 import org.kuali.rice.core.api.encryption.EncryptionService;
021 import org.kuali.rice.kew.api.KewApiConstants;
022 import org.kuali.rice.kew.api.WorkflowDocument;
023 import org.kuali.rice.krad.UserSession;
024 import org.kuali.rice.krad.bo.SessionDocument;
025 import org.kuali.rice.krad.dao.SessionDocumentDao;
026 import org.kuali.rice.krad.datadictionary.DocumentEntry;
027 import org.kuali.rice.krad.service.BusinessObjectService;
028 import org.kuali.rice.krad.service.DataDictionaryService;
029 import org.kuali.rice.krad.service.KRADServiceLocatorWeb;
030 import org.kuali.rice.krad.service.SessionDocumentService;
031 import org.kuali.rice.krad.web.form.DocumentFormBase;
032 import org.springframework.transaction.annotation.Transactional;
033
034 import java.io.ByteArrayInputStream;
035 import java.io.ByteArrayOutputStream;
036 import java.io.ObjectInputStream;
037 import java.io.ObjectOutputStream;
038 import java.sql.Timestamp;
039 import java.util.HashMap;
040 import java.util.Map;
041
042 /**
043 * Implementation of <code>SessionDocumentService</code> that persists the document form
044 * contents to the underlying database
045 *
046 * @author Kuali Rice Team (rice.collab@kuali.org)
047 */
048 @Transactional
049 public class SessionDocumentServiceImpl implements SessionDocumentService {
050 private static org.apache.log4j.Logger LOG = org.apache.log4j.Logger.getLogger(SessionDocumentServiceImpl.class);
051
052 protected static final String IP_ADDRESS = "ipAddress";
053 protected static final String PRINCIPAL_ID = "principalId";
054 protected static final String DOCUMENT_NUMBER = "documentNumber";
055 protected static final String SESSION_ID = "sessionId";
056
057 private EncryptionService encryptionService;
058
059 private BusinessObjectService businessObjectService;
060 private DataDictionaryService dataDictionaryService;
061 private SessionDocumentDao sessionDocumentDao;
062
063 @Override
064 public DocumentFormBase getDocumentForm(String documentNumber, String docFormKey, UserSession userSession,
065 String ipAddress) {
066 DocumentFormBase documentForm = null;
067
068 LOG.debug("getDocumentForm DocumentFormBase from db");
069 try {
070 // re-create the DocumentFormBase object
071 documentForm = (DocumentFormBase) retrieveDocumentForm(userSession, docFormKey, documentNumber, ipAddress);
072
073 //re-store workFlowDocument into session
074 WorkflowDocument workflowDocument =
075 documentForm.getDocument().getDocumentHeader().getWorkflowDocument();
076 addDocumentToUserSession(userSession, workflowDocument);
077 } catch (Exception e) {
078 LOG.error("getDocumentForm failed for SessId/DocNum/PrinId/IP:" + userSession.getKualiSessionId() + "/" +
079 documentNumber + "/" + userSession.getPrincipalId() + "/" + ipAddress, e);
080 }
081
082 return documentForm;
083 }
084
085 protected Object retrieveDocumentForm(UserSession userSession, String sessionId, String documentNumber,
086 String ipAddress) throws Exception {
087 HashMap<String, String> primaryKeys = new HashMap<String, String>(4);
088 primaryKeys.put(SESSION_ID, sessionId);
089 if (documentNumber != null) {
090 primaryKeys.put(DOCUMENT_NUMBER, documentNumber);
091 }
092 primaryKeys.put(PRINCIPAL_ID, userSession.getPrincipalId());
093 primaryKeys.put(IP_ADDRESS, ipAddress);
094
095 SessionDocument sessionDoc = getBusinessObjectService().findByPrimaryKey(SessionDocument.class, primaryKeys);
096 if (sessionDoc != null) {
097 byte[] formAsBytes = sessionDoc.getSerializedDocumentForm();
098 if (sessionDoc.isEncrypted()) {
099 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 }