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