1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 package org.kuali.rice.krad.service.impl;
17
18 import org.apache.commons.lang.StringUtils;
19 import org.apache.log4j.Logger;
20 import org.kuali.rice.core.api.config.property.ConfigurationService;
21 import org.kuali.rice.krad.bo.Attachment;
22 import org.kuali.rice.krad.bo.Note;
23 import org.kuali.rice.krad.bo.PersistableBusinessObject;
24 import org.kuali.rice.krad.dao.AttachmentDao;
25 import org.kuali.rice.krad.document.Document;
26 import org.kuali.rice.krad.service.AttachmentService;
27 import org.kuali.rice.krad.util.KRADConstants;
28 import org.springframework.transaction.annotation.Transactional;
29
30 import java.io.BufferedInputStream;
31 import java.io.BufferedOutputStream;
32 import java.io.File;
33 import java.io.FileInputStream;
34 import java.io.FileOutputStream;
35 import java.io.IOException;
36 import java.io.InputStream;
37 import java.util.UUID;
38
39
40
41
42 @Transactional
43 public class AttachmentServiceImpl implements AttachmentService {
44 private static final int MAX_DIR_LEVELS = 6;
45 private static Logger LOG = Logger.getLogger(AttachmentServiceImpl.class);
46
47 private ConfigurationService kualiConfigurationService;
48 private AttachmentDao attachmentDao;
49
50
51
52
53
54 public Attachment getAttachmentByNoteId(Long noteId) {
55 return attachmentDao.getAttachmentByNoteId(noteId);
56 }
57
58
59
60
61
62 public Attachment createAttachment(PersistableBusinessObject parent, String uploadedFileName, String mimeType, int fileSize, InputStream fileContents, String attachmentTypeCode) throws IOException {
63 if ( LOG.isDebugEnabled() ) {
64 LOG.debug("starting to create attachment for document: " + parent.getObjectId());
65 }
66 if (parent == null) {
67 throw new IllegalArgumentException("invalid (null or uninitialized) document");
68 }
69 if (StringUtils.isBlank(uploadedFileName)) {
70 throw new IllegalArgumentException("invalid (blank) fileName");
71 }
72 if (StringUtils.isBlank(mimeType)) {
73 throw new IllegalArgumentException("invalid (blank) mimeType");
74 }
75 if (fileSize <= 0) {
76 throw new IllegalArgumentException("invalid (non-positive) fileSize");
77 }
78 if (fileContents == null) {
79 throw new IllegalArgumentException("invalid (null) inputStream");
80 }
81
82 String uniqueFileNameGuid = UUID.randomUUID().toString();
83 String fullPathUniqueFileName = getDocumentDirectory(parent.getObjectId()) + File.separator + uniqueFileNameGuid;
84
85 writeInputStreamToFileStorage(fileContents, fullPathUniqueFileName);
86
87
88 Attachment attachment = new Attachment();
89 attachment.setAttachmentIdentifier(uniqueFileNameGuid);
90 attachment.setAttachmentFileName(uploadedFileName);
91 attachment.setAttachmentFileSize(new Long(fileSize));
92 attachment.setAttachmentMimeTypeCode(mimeType);
93 attachment.setAttachmentTypeCode(attachmentTypeCode);
94
95 LOG.debug("finished creating attachment for document: " + parent.getObjectId());
96 return attachment;
97 }
98
99 private void writeInputStreamToFileStorage(InputStream fileContents, String fullPathUniqueFileName) throws IOException {
100 File fileOut = new File(fullPathUniqueFileName);
101 FileOutputStream streamOut = null;
102 BufferedOutputStream bufferedStreamOut = null;
103 try {
104 streamOut = new FileOutputStream(fileOut);
105 bufferedStreamOut = new BufferedOutputStream(streamOut);
106 int c;
107 while ((c = fileContents.read()) != -1) {
108 bufferedStreamOut.write(c);
109 }
110 }
111 finally {
112 bufferedStreamOut.close();
113 streamOut.close();
114 }
115 }
116
117 public void moveAttachmentWherePending(Note note) {
118 if (note == null) {
119 throw new IllegalArgumentException("Note must be non-null");
120 }
121 if (StringUtils.isBlank(note.getObjectId())) {
122 throw new IllegalArgumentException("Note does not have a valid object id, object id was null or empty");
123 }
124 Attachment attachment = note.getAttachment();
125 if(attachment!=null){
126 try {
127 moveAttachmentFromPending(attachment, note.getRemoteObjectIdentifier());
128 }
129 catch (IOException e) {
130 throw new RuntimeException("Problem moving pending attachment to final directory");
131 }
132 }
133 }
134
135 private void moveAttachmentFromPending(Attachment attachment, String objectId) throws IOException {
136
137 String fullPendingFileName = getPendingDirectory() + File.separator + attachment.getAttachmentIdentifier();
138 File pendingFile = new File(fullPendingFileName);
139
140 if(pendingFile.exists()) {
141 BufferedInputStream bufferedStream = null;
142 FileInputStream oldFileStream = null;
143 String fullPathNewFile = getDocumentDirectory(objectId) + File.separator + attachment.getAttachmentIdentifier();
144 try {
145 oldFileStream = new FileInputStream(pendingFile);
146 bufferedStream = new BufferedInputStream(oldFileStream);
147 writeInputStreamToFileStorage(bufferedStream,fullPathNewFile);
148 }
149 finally {
150
151 bufferedStream.close();
152 oldFileStream.close();
153
154 pendingFile.delete();
155
156 }
157 }
158
159 }
160
161 public void deleteAttachmentContents(Attachment attachment) {
162 if (attachment.getNote() == null) throw new RuntimeException("Attachment.note must be set in order to delete the attachment");
163 String fullPathUniqueFileName = getDocumentDirectory(attachment.getNote().getRemoteObjectIdentifier()) + File.separator + attachment.getAttachmentIdentifier();
164 File attachmentFile = new File(fullPathUniqueFileName);
165 attachmentFile.delete();
166 }
167 private String getPendingDirectory() {
168 return this.getDocumentDirectory("");
169 }
170
171 private String getDocumentDirectory(String objectId) {
172
173 File documentDirectory = new File(getDocumentFileStorageLocation(objectId));
174 if (!documentDirectory.exists()) {
175 boolean success = documentDirectory.mkdirs();
176 if (!success) {
177 throw new RuntimeException("Could not generate directory for File at: " + documentDirectory.getAbsolutePath());
178 }
179 }
180 return documentDirectory.getAbsolutePath();
181 }
182
183
184
185
186
187
188 public InputStream retrieveAttachmentContents(Attachment attachment) throws IOException {
189
190 if(attachment.getNoteIdentifier()!=null) {
191 attachment.refreshNonUpdateableReferences();
192 }
193
194 String parentDirectory = "";
195 if(attachment.getNote()!=null && attachment.getNote().getRemoteObjectIdentifier() != null) {
196 parentDirectory = attachment.getNote().getRemoteObjectIdentifier();
197 }
198
199 return new BufferedInputStream(new FileInputStream(getDocumentDirectory(parentDirectory) + File.separator + attachment.getAttachmentIdentifier()));
200 }
201
202 private String getDocumentFileStorageLocation(String objectId) {
203 String location = null;
204 if(StringUtils.isEmpty(objectId)) {
205 location = kualiConfigurationService.getPropertyValueAsString(
206 KRADConstants.ATTACHMENTS_PENDING_DIRECTORY_KEY);
207 } else {
208
209
210
211
212
213
214 char[] chars = objectId.toUpperCase().replace(" ", "").toCharArray();
215 int count = chars.length < MAX_DIR_LEVELS ? chars.length : MAX_DIR_LEVELS;
216
217 StringBuffer prefix = new StringBuffer();
218 for ( int i = 0; i < count; i++ )
219 prefix.append(File.separator + chars[i]);
220
221 location = kualiConfigurationService.getPropertyValueAsString(KRADConstants.ATTACHMENTS_DIRECTORY_KEY) + prefix + File.separator + objectId;
222 }
223 return location;
224 }
225
226
227
228
229 public void deletePendingAttachmentsModifiedBefore(long modificationTime) {
230 String pendingAttachmentDirName = getPendingDirectory();
231 if (StringUtils.isBlank(pendingAttachmentDirName)) {
232 throw new RuntimeException("Blank pending attachment directory name");
233 }
234 File pendingAttachmentDir = new File(pendingAttachmentDirName);
235 if (!pendingAttachmentDir.exists()) {
236 throw new RuntimeException("Pending attachment directory does not exist");
237 }
238 if (!pendingAttachmentDir.isDirectory()) {
239 throw new RuntimeException("Pending attachment directory is not a directory! " + pendingAttachmentDir.getAbsolutePath());
240 }
241
242 File[] files = pendingAttachmentDir.listFiles();
243 for (File file : files) {
244 if (!file.getName().equals("placeholder.txt")) {
245 if (file.lastModified() < modificationTime) {
246 file.delete();
247 }
248 }
249 }
250
251 }
252
253
254
255
256
257
258
259 public void setAttachmentDao(AttachmentDao d) {
260 this.attachmentDao = d;
261 }
262
263
264
265
266 public AttachmentDao getAttachmentDao() {
267 return attachmentDao;
268 }
269
270
271
272
273
274 public ConfigurationService getKualiConfigurationService() {
275 return kualiConfigurationService;
276 }
277
278
279
280
281
282 public void setKualiConfigurationService(ConfigurationService configService) {
283 this.kualiConfigurationService = configService;
284 }
285 }