001/** 002 * Copyright 2005-2016 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 */ 016package org.kuali.rice.krad.service.impl; 017 018import org.apache.commons.lang.StringUtils; 019import org.kuali.rice.core.api.CoreApiServiceLocator; 020import org.kuali.rice.core.api.encryption.EncryptionService; 021import org.kuali.rice.kew.api.WorkflowDocument; 022import org.kuali.rice.krad.UserSession; 023import org.kuali.rice.krad.UserSessionUtils; 024import org.kuali.rice.krad.bo.SessionDocument; 025import org.kuali.rice.krad.dao.SessionDocumentDao; 026import org.kuali.rice.krad.datadictionary.DocumentEntry; 027import org.kuali.rice.krad.service.BusinessObjectService; 028import org.kuali.rice.krad.service.DataDictionaryService; 029import org.kuali.rice.krad.service.KRADServiceLocatorWeb; 030import org.kuali.rice.krad.service.SessionDocumentService; 031import org.kuali.rice.krad.web.form.DocumentFormBase; 032import org.springframework.transaction.annotation.Transactional; 033 034import java.io.ByteArrayInputStream; 035import java.io.ByteArrayOutputStream; 036import java.io.ObjectInputStream; 037import java.io.ObjectOutputStream; 038import java.sql.Timestamp; 039import java.util.HashMap; 040 041/** 042 * Implementation of <code>SessionDocumentService</code> that persists the document form 043 * contents to the underlying database 044 * 045 * @author Kuali Rice Team (rice.collab@kuali.org) 046 */ 047@Transactional 048public class SessionDocumentServiceImpl implements SessionDocumentService { 049 private static org.apache.log4j.Logger LOG = org.apache.log4j.Logger.getLogger(SessionDocumentServiceImpl.class); 050 051 protected static final String IP_ADDRESS = "ipAddress"; 052 protected static final String PRINCIPAL_ID = "principalId"; 053 protected static final String DOCUMENT_NUMBER = "documentNumber"; 054 protected static final String SESSION_ID = "sessionId"; 055 056 private EncryptionService encryptionService; 057 058 private BusinessObjectService businessObjectService; 059 private DataDictionaryService dataDictionaryService; 060 private SessionDocumentDao sessionDocumentDao; 061 062 @Override 063 public DocumentFormBase getDocumentForm(String documentNumber, String docFormKey, UserSession userSession, 064 String ipAddress) { 065 DocumentFormBase documentForm = null; 066 067 LOG.debug("getDocumentForm DocumentFormBase from db"); 068 try { 069 // re-create the DocumentFormBase object 070 documentForm = (DocumentFormBase) retrieveDocumentForm(userSession, docFormKey, documentNumber, ipAddress); 071 072 //re-store workFlowDocument into session 073 WorkflowDocument workflowDocument = 074 documentForm.getDocument().getDocumentHeader().getWorkflowDocument(); 075 UserSessionUtils.addWorkflowDocument(userSession, workflowDocument); 076 } catch (Exception e) { 077 LOG.error("getDocumentForm failed for SessId/DocNum/PrinId/IP:" + userSession.getKualiSessionId() + "/" + 078 documentNumber + "/" + userSession.getPrincipalId() + "/" + ipAddress, e); 079 } 080 081 return documentForm; 082 } 083 084 protected Object retrieveDocumentForm(UserSession userSession, String sessionId, String documentNumber, 085 String ipAddress) throws Exception { 086 HashMap<String, String> primaryKeys = new HashMap<String, String>(4); 087 primaryKeys.put(SESSION_ID, sessionId); 088 if (documentNumber != null) { 089 primaryKeys.put(DOCUMENT_NUMBER, documentNumber); 090 } 091 primaryKeys.put(PRINCIPAL_ID, userSession.getPrincipalId()); 092 primaryKeys.put(IP_ADDRESS, ipAddress); 093 094 SessionDocument sessionDoc = getBusinessObjectService().findByPrimaryKey(SessionDocument.class, primaryKeys); 095 if (sessionDoc != null) { 096 byte[] formAsBytes = sessionDoc.getSerializedDocumentForm(); 097 if (sessionDoc.isEncrypted()) { 098 formAsBytes = getEncryptionService().decryptBytes(formAsBytes); 099 } 100 ByteArrayInputStream baip = new ByteArrayInputStream(formAsBytes); 101 ObjectInputStream ois = new ObjectInputStream(baip); 102 103 return ois.readObject(); 104 } 105 106 return null; 107 } 108 109 @Override 110 public WorkflowDocument getDocumentFromSession(UserSession userSession, String docId) { 111 return UserSessionUtils.getWorkflowDocument(userSession, docId); 112 } 113 114 /** 115 * @see org.kuali.rice.krad.service.SessionDocumentService#addDocumentToUserSession(org.kuali.rice.krad.UserSession, 116 * org.kuali.rice.kew.api.WorkflowDocument) 117 */ 118 @Override 119 public void addDocumentToUserSession(UserSession userSession, WorkflowDocument document) { 120 UserSessionUtils.addWorkflowDocument(userSession, document); 121 } 122 123 /** 124 * @see org.kuali.rice.krad.service.SessionDocumentService#purgeDocumentForm(String, String, 125 * org.kuali.rice.krad.UserSession, String) 126 */ 127 @Override 128 public void purgeDocumentForm(String documentNumber, String docFormKey, UserSession userSession, String ipAddress) { 129 synchronized (userSession) { 130 131 LOG.debug("purge document form from session"); 132 userSession.removeObject(docFormKey); 133 try { 134 LOG.debug("purge document form from database"); 135 HashMap<String, String> primaryKeys = new HashMap<String, String>(4); 136 primaryKeys.put(SESSION_ID, userSession.getKualiSessionId()); 137 primaryKeys.put(DOCUMENT_NUMBER, documentNumber); 138 primaryKeys.put(PRINCIPAL_ID, userSession.getPrincipalId()); 139 primaryKeys.put(IP_ADDRESS, ipAddress); 140 getBusinessObjectService().deleteMatching(SessionDocument.class, primaryKeys); 141 } catch (Exception e) { 142 LOG.error("purgeDocumentForm failed for SessId/DocNum/PrinId/IP:" + userSession.getKualiSessionId() + 143 "/" + documentNumber + "/" + userSession.getPrincipalId() + "/" + ipAddress, e); 144 } 145 } 146 } 147 148 @Override 149 public void setDocumentForm(DocumentFormBase form, UserSession userSession, String ipAddress) { 150 synchronized (userSession) { 151 //formKey was set in KualiDocumentActionBase execute method 152 String formKey = form.getFormKey(); 153 String key = userSession.getKualiSessionId() + "-" + formKey; 154 155 String documentNumber = form.getDocument().getDocumentNumber(); 156 if (StringUtils.isNotBlank(formKey)) { 157 //FIXME: Currently using formKey for sessionId 158 persistDocumentForm(form, userSession, ipAddress, formKey, documentNumber); 159 } else { 160 LOG.warn("documentNumber is null on form's document: " + form); 161 } 162 } 163 } 164 165 protected void persistDocumentForm(DocumentFormBase form, UserSession userSession, String ipAddress, 166 String sessionId, String documentNumber) { 167 try { 168 LOG.debug("set Document Form into database"); 169 170 Timestamp currentTime = new Timestamp(System.currentTimeMillis()); 171 ByteArrayOutputStream baos = new ByteArrayOutputStream(); 172 ObjectOutputStream oos = new ObjectOutputStream(baos); 173 oos.writeObject(form); 174 175 // serialize the DocumentFormBase object into a byte array 176 byte[] formAsBytes = baos.toByteArray(); 177 boolean encryptContent = false; 178 DocumentEntry documentEntry = 179 getDataDictionaryService().getDataDictionary().getDocumentEntry(form.getDocTypeName()); 180 if (documentEntry != null) { 181 encryptContent = documentEntry.isEncryptDocumentDataInPersistentSessionStorage(); 182 } 183 184 if (encryptContent) { 185 formAsBytes = getEncryptionService().encryptBytes(formAsBytes); 186 } 187 188 // check if a record is already there in the database 189 // this may only happen under jMeter testing, but there is no way to be sure 190 HashMap<String, String> primaryKeys = new HashMap<String, String>(4); 191 primaryKeys.put(SESSION_ID, sessionId); 192 primaryKeys.put(DOCUMENT_NUMBER, documentNumber); 193 primaryKeys.put(PRINCIPAL_ID, userSession.getPrincipalId()); 194 primaryKeys.put(IP_ADDRESS, ipAddress); 195 196 SessionDocument sessionDocument = 197 getBusinessObjectService().findByPrimaryKey(SessionDocument.class, primaryKeys); 198 if (sessionDocument == null) { 199 sessionDocument = new SessionDocument(); 200 sessionDocument.setSessionId(sessionId); 201 sessionDocument.setDocumentNumber(documentNumber); 202 sessionDocument.setPrincipalId(userSession.getPrincipalId()); 203 sessionDocument.setIpAddress(ipAddress); 204 } 205 sessionDocument.setSerializedDocumentForm(formAsBytes); 206 sessionDocument.setEncrypted(encryptContent); 207 sessionDocument.setLastUpdatedDate(currentTime); 208 209 businessObjectService.save(sessionDocument); 210 } catch (Exception e) { 211 final String className = form != null ? form.getClass().getName() : "null"; 212 LOG.error("setDocumentForm failed for SessId/DocNum/PrinId/IP/class:" + userSession.getKualiSessionId() + 213 "/" + documentNumber + "/" + userSession.getPrincipalId() + "/" + ipAddress + "/" + className, e); 214 } 215 } 216 217 /** 218 * @see org.kuali.rice.krad.service.SessionDocumentService#purgeAllSessionDocuments(java.sql.Timestamp) 219 */ 220 @Override 221 public void purgeAllSessionDocuments(Timestamp expirationDate) { 222 sessionDocumentDao.purgeAllSessionDocuments(expirationDate); 223 } 224 225 protected SessionDocumentDao getSessionDocumentDao() { 226 return this.sessionDocumentDao; 227 } 228 229 public void setSessionDocumentDao(SessionDocumentDao sessionDocumentDao) { 230 this.sessionDocumentDao = sessionDocumentDao; 231 } 232 233 protected BusinessObjectService getBusinessObjectService() { 234 return this.businessObjectService; 235 } 236 237 public void setBusinessObjectService(BusinessObjectService businessObjectService) { 238 this.businessObjectService = businessObjectService; 239 } 240 241 protected EncryptionService getEncryptionService() { 242 if (encryptionService == null) { 243 encryptionService = CoreApiServiceLocator.getEncryptionService(); 244 } 245 return encryptionService; 246 } 247 248 protected DataDictionaryService getDataDictionaryService() { 249 if (dataDictionaryService == null) { 250 dataDictionaryService = KRADServiceLocatorWeb.getDataDictionaryService(); 251 } 252 return dataDictionaryService; 253 } 254}