001/**
002 * Copyright 2005-2018 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.kns.document;
017
018import org.apache.commons.codec.digest.DigestUtils;
019import org.apache.commons.collections.CollectionUtils;
020import org.apache.commons.lang.StringUtils;
021import org.apache.ojb.broker.core.proxy.ProxyHelper;
022import org.apache.struts.upload.FormFile;
023import org.kuali.rice.kns.maintenance.Maintainable;
024import org.kuali.rice.krad.bo.DocumentAttachment;
025import org.kuali.rice.kns.bo.GlobalBusinessObject;
026import org.kuali.rice.krad.bo.MultiDocumentAttachment;
027import org.kuali.rice.krad.bo.PersistableAttachment;
028import org.kuali.rice.krad.bo.PersistableAttachmentBase;
029import org.kuali.rice.krad.bo.PersistableAttachmentList;
030import org.kuali.rice.krad.rules.rule.event.DocumentEvent;
031import org.kuali.rice.krad.rules.rule.event.SaveDocumentEvent;
032import org.kuali.rice.krad.service.BusinessObjectSerializerService;
033import org.kuali.rice.krad.service.KRADServiceLocator;
034import org.kuali.rice.krad.service.KRADServiceLocatorWeb;
035import org.kuali.rice.krad.util.KRADUtils;
036import org.kuali.rice.krad.util.ObjectUtils;
037
038import javax.persistence.Transient;
039import java.io.FileNotFoundException;
040import java.io.IOException;
041import java.lang.reflect.Method;
042import java.util.ArrayList;
043import java.util.Collections;
044import java.util.HashMap;
045import java.util.List;
046import java.util.Map;
047import java.util.UUID;
048
049/**
050 * @author Kuali Rice Team (rice.collab@kuali.org)
051 *
052 * @deprecated Use {@link org.kuali.rice.krad.maintenance.MaintenanceDocumentBase}.
053 */
054@Deprecated
055public class MaintenanceDocumentBase extends org.kuali.rice.krad.maintenance.MaintenanceDocumentBase implements MaintenanceDocument {
056    private static final org.apache.log4j.Logger LOG = org.apache.log4j.Logger.getLogger(MaintenanceDocumentBase.class);
057
058    @Transient
059    protected transient FormFile fileAttachment;
060
061    public MaintenanceDocumentBase() {
062        super();
063    }
064
065    public MaintenanceDocumentBase(String documentTypeName) {
066        super(documentTypeName);
067    }
068
069    @Override
070    public Object getDocumentBusinessObject() {
071        return super.getDocumentDataObject();
072    }
073
074    /**
075     * Checks old maintainable bo has key values
076     */
077    public boolean isOldBusinessObjectInDocument() {
078        boolean isOldBusinessObjectInExistence = false;
079        if (getOldMaintainableObject() == null || getOldMaintainableObject().getBusinessObject() == null) {
080            isOldBusinessObjectInExistence = false;
081        } else {
082            isOldBusinessObjectInExistence = getOldMaintainableObject().isOldBusinessObjectInDocument();
083        }
084        return isOldBusinessObjectInExistence;
085    }
086
087    public Maintainable getNewMaintainableObject() {
088        return (Maintainable) newMaintainableObject;
089    }
090
091    public Maintainable getOldMaintainableObject() {
092        return (Maintainable) oldMaintainableObject;
093    }
094
095    public FormFile getFileAttachment() {
096        return this.fileAttachment;
097    }
098
099    public void setFileAttachment(FormFile fileAttachment) {
100        this.fileAttachment = fileAttachment;
101    }
102
103    /**
104     * The attachment BO is proxied in OJB.  For some reason when an attachment does not yet exist,
105     * refreshReferenceObject is not returning null and the proxy cannot be materialized. So, this method exists to
106     * properly handle the proxied attachment BO.  This is a hack and should be removed post JPA migration.
107     */
108    protected void refreshAttachment() {
109        if (KRADUtils.isNull(attachment)) {
110            this.refreshReferenceObject("attachment");
111            final boolean isProxy = attachment != null && ProxyHelper.isProxy(attachment);
112            if (isProxy && ProxyHelper.getRealObject(attachment) == null) {
113                attachment = null;
114            }
115        }
116    }
117
118    protected void refreshAttachmentList() {
119        if (KRADUtils.isNull(attachments)) {
120            this.refreshReferenceObject("attachments");
121            final boolean isProxy = attachments != null && ProxyHelper.isProxy(attachments);
122            if (isProxy && ProxyHelper.getRealObject(attachments) == null) {
123                attachments = null;
124            }
125        }
126    }
127
128    @Override
129    public void populateDocumentAttachment() {
130        refreshAttachment();
131
132        if (fileAttachment != null && StringUtils.isNotEmpty(fileAttachment.getFileName())) {
133            //Populate DocumentAttachment BO
134            if (attachment == null) {
135                attachment = new DocumentAttachment();
136            }
137
138            byte[] fileContents;
139            try {
140                fileContents = fileAttachment.getFileData();
141                if (fileContents.length > 0) {
142                    attachment.setFileName(fileAttachment.getFileName());
143                    attachment.setContentType(fileAttachment.getContentType());
144                    attachment.setAttachmentContent(fileAttachment.getFileData());
145                    attachment.setObjectId(UUID.randomUUID().toString());
146                    PersistableAttachment boAttachment = (PersistableAttachment) newMaintainableObject.getDataObject();
147                    boAttachment.setAttachmentContent(null);
148                    attachment.setDocumentNumber(getDocumentNumber());
149                }
150            } catch (FileNotFoundException e) {
151                LOG.error("Error while populating the Document Attachment", e);
152                throw new RuntimeException("Could not populate DocumentAttachment object", e);
153            } catch (IOException e) {
154                LOG.error("Error while populating the Document Attachment", e);
155                throw new RuntimeException("Could not populate DocumentAttachment object", e);
156            }
157        } else {
158            //fileAttachment isn't filled, populate from bo if it exists
159            PersistableAttachment boAttachment = (PersistableAttachment) newMaintainableObject.getDataObject();
160            if (attachment == null
161                    && boAttachment != null
162                    && boAttachment.getAttachmentContent() != null) {
163                DocumentAttachment newAttachment = new DocumentAttachment();
164                newAttachment.setDocumentNumber(getDocumentNumber());
165                newAttachment.setAttachmentContent(boAttachment.getAttachmentContent());
166                newAttachment.setContentType(boAttachment.getContentType());
167                newAttachment.setFileName(boAttachment.getFileName());
168                //null out boAttachment file, will be copied back before final save.
169                boAttachment.setAttachmentContent(null);
170                attachment = newAttachment;
171            }
172        }
173    }
174
175    @Override
176    public void populateAttachmentForBO() {
177        refreshAttachment();
178
179        PersistableAttachment boAttachment = (PersistableAttachment) newMaintainableObject.getDataObject();
180
181        if (ObjectUtils.isNotNull(getAttachmentPropertyName())) {
182                String attachmentPropNm = getAttachmentPropertyName();
183                String attachmentPropNmSetter = "get" + attachmentPropNm.substring(0, 1).toUpperCase() + attachmentPropNm.substring(1, attachmentPropNm.length());
184                FormFile attachmentFromBusinessObject;
185
186                if((boAttachment.getFileName() == null) && (boAttachment instanceof PersistableAttachment)) {
187                        try {
188                                Method[] methods = boAttachment.getClass().getMethods();
189                                for (Method method : methods) {
190                                        if (method.getName().equals(attachmentPropNmSetter)) {
191                                                attachmentFromBusinessObject =  (FormFile)(boAttachment.getClass().getDeclaredMethod(attachmentPropNmSetter).invoke(boAttachment));
192                                                if (attachmentFromBusinessObject != null) {
193                                                        //boAttachment.setAttachmentContent(attachmentFromBusinessObject.getFileData());
194                                                        boAttachment.setFileName(attachmentFromBusinessObject.getFileName());
195                                                        boAttachment.setContentType(attachmentFromBusinessObject.getContentType());
196                                                }
197                                                break;
198                                        }
199                                }
200                   } catch (Exception e) {
201                                LOG.error("Not able to get the attachment " + e.getMessage());
202                                throw new RuntimeException("Not able to get the attachment " + e.getMessage());
203                   }
204          }
205      }
206
207      if((boAttachment.getFileName() == null) && (boAttachment instanceof PersistableAttachment) && (attachment != null)) {
208          //byte[] fileContents;
209          //fileContents = attachment.getAttachmentContent();
210          if (attachment.getFileName() != null) {
211              boAttachment.setAttachmentContent(null);
212              boAttachment.setFileName(attachment.getFileName());
213              boAttachment.setContentType(attachment.getContentType());
214          }
215       }
216    }
217
218    @Override
219    public void populateAttachmentBeforeSave() {
220        PersistableAttachment boAttachment = (PersistableAttachment) newMaintainableObject.getDataObject();
221        if (attachment != null
222                && attachment.getAttachmentContent() != null) {
223            boAttachment.setAttachmentContent(attachment.getAttachmentContent());
224        } else {
225            boAttachment.setAttachmentContent(null);
226            boAttachment.setFileName(null);
227            boAttachment.setContentType(null);
228        }
229    }
230
231    @Override
232    public void populateBoAttachmentListBeforeSave() {
233
234        PersistableAttachmentList<PersistableAttachment> boAttachments = (PersistableAttachmentList<PersistableAttachment>) newMaintainableObject.getDataObject();
235        if (CollectionUtils.isEmpty(attachments)) {
236            //there are no attachments.  Clear out Bo Attachments
237            boAttachments.setAttachments(Collections.<PersistableAttachment>emptyList());
238            return;
239        }
240        Map<String, MultiDocumentAttachment> files = new HashMap<String, MultiDocumentAttachment>();
241        for (MultiDocumentAttachment multiAttach : attachments) {
242            String key = new StringBuffer(multiAttach.getFileName()).append("|").append(multiAttach.getContentType()).toString();
243            files.put(key, multiAttach);
244        }
245
246
247        //want to just copy over file if possible, as there can be other fields that are not on PersistableAttachment
248        //these arrays should be somewhat synched by the other populate methods
249        if (CollectionUtils.isNotEmpty(boAttachments.getAttachments())) {
250            for (PersistableAttachment attach : boAttachments.getAttachments()) {
251                //try to get a new instance of the correct object...
252                String key = new StringBuffer(attach.getFileName()).append("|").append(attach.getContentType()).toString();
253                if (files.containsKey(key)) {
254                    attach.setAttachmentContent(files.get(key).getAttachmentContent());
255                    files.remove(key);
256                }
257            }
258        }
259    }
260
261    @Override
262    public void populateAttachmentListForBO() {
263        refreshAttachmentList();
264
265        PersistableAttachmentList<PersistableAttachment> boAttachments = (PersistableAttachmentList<PersistableAttachment>) newMaintainableObject.getDataObject();
266
267        if (ObjectUtils.isNotNull(getAttachmentListPropertyName())) {
268            //String collectionName = getAttachmentCollectionName();
269            String attachmentPropNm = getAttachmentListPropertyName();
270            String attachmentPropNmSetter = "get" + attachmentPropNm.substring(0, 1).toUpperCase() + attachmentPropNm.substring(1, attachmentPropNm.length());
271
272
273            for (PersistableAttachment persistableAttachment : boAttachments.getAttachments()) {
274                if((persistableAttachment.getFileName() == null)) {
275                    try {
276                        FormFile attachmentFromBusinessObject =  (FormFile)(persistableAttachment.getClass().getDeclaredMethod(attachmentPropNmSetter).invoke(persistableAttachment));
277                        if (attachmentFromBusinessObject != null) {
278                            //persistableAttachment.setAttachmentContent(
279                            //        attachmentFromBusinessObject.getFileData());
280                            persistableAttachment.setFileName(attachmentFromBusinessObject.getFileName());
281                            persistableAttachment.setContentType(attachmentFromBusinessObject.getContentType());
282                        }
283                    } catch (Exception e) {
284                        LOG.error("Not able to get the attachment " + e.getMessage());
285                        throw new RuntimeException("Not able to get the attachment " + e.getMessage());
286                    }
287                }
288            }
289        }
290        if((CollectionUtils.isEmpty(boAttachments.getAttachments())
291                && (CollectionUtils.isNotEmpty(attachments)))) {
292
293            List<PersistableAttachment> attachmentList = new ArrayList<PersistableAttachment>();
294            for (MultiDocumentAttachment multiAttach : attachments) {
295
296                //try to get a new instance of the correct object...
297                if (multiAttach.getAttachmentContent().length > 0) {
298                    PersistableAttachment persistableAttachment = convertDocToBoAttachment(multiAttach, false);
299                    attachmentList.add(persistableAttachment);
300                }
301            }
302            boAttachments.setAttachments(attachmentList);
303        }
304    }
305
306    private PersistableAttachment convertDocToBoAttachment(MultiDocumentAttachment multiAttach, boolean copyFile) {
307        PersistableAttachment persistableAttachment = new PersistableAttachmentBase();
308
309        if (copyFile
310                && multiAttach.getAttachmentContent() != null) {
311            persistableAttachment.setAttachmentContent(multiAttach.getAttachmentContent());
312        }
313        persistableAttachment.setFileName(multiAttach.getFileName());
314        persistableAttachment.setContentType(multiAttach.getContentType());
315        return persistableAttachment;
316    }
317
318    @Override
319    public void populateDocumentAttachmentList() {
320        refreshAttachmentList();
321
322        String attachmentPropNm = getAttachmentListPropertyName();
323        String attachmentPropNmSetter = "get" + attachmentPropNm.substring(0, 1).toUpperCase() + attachmentPropNm.substring(1, attachmentPropNm.length());
324        //don't have form fields to use to fill, but they should be populated on the DataObject.  grab them from there.
325        PersistableAttachmentList<PersistableAttachment> boAttachmentList = (PersistableAttachmentList<PersistableAttachment>) newMaintainableObject.getDataObject();
326
327        if (CollectionUtils.isNotEmpty(boAttachmentList.getAttachments())) {
328
329
330            //build map for comparison
331            Map<String, MultiDocumentAttachment> md5Hashes = new HashMap<String, MultiDocumentAttachment>();
332            if (CollectionUtils.isNotEmpty(attachments)) {
333                for (MultiDocumentAttachment currentAttachment : attachments) {
334                    md5Hashes.put(DigestUtils.md5Hex(currentAttachment.getAttachmentContent()), currentAttachment);
335                }
336            }
337
338            //Populate DocumentAttachment BO
339            attachments = new ArrayList<MultiDocumentAttachment>();
340
341            for (PersistableAttachment persistableAttachment : boAttachmentList.getAttachments()) {
342                try {
343                    FormFile attachmentFromBusinessObject =  (FormFile)(persistableAttachment.getClass().getDeclaredMethod(attachmentPropNmSetter).invoke(persistableAttachment));
344                    if (attachmentFromBusinessObject != null) {
345                        //
346                        //byte[] fileContents = attachmentFromBusinessObject.getFileData();
347                        String md5Hex = DigestUtils.md5Hex(attachmentFromBusinessObject.getInputStream());
348                        if (md5Hashes.containsKey(md5Hex)) {
349                            String newFileName = attachmentFromBusinessObject.getFileName();
350                            MultiDocumentAttachment multiAttach = md5Hashes.get(md5Hex);
351                            if (multiAttach.getFileName().equals(newFileName)) {
352                                attachments.add(multiAttach);
353                            } else {
354                                multiAttach.setFileName(attachmentFromBusinessObject.getFileName());
355                                multiAttach.setContentType(attachmentFromBusinessObject.getContentType());
356                                multiAttach.setObjectId(UUID.randomUUID().toString());
357                                attachments.add(multiAttach);
358                            }
359                            md5Hashes.remove(md5Hex);
360                        } else {
361                            MultiDocumentAttachment attach = new MultiDocumentAttachment();
362                            attach.setFileName(attachmentFromBusinessObject.getFileName());
363                            attach.setContentType(attachmentFromBusinessObject.getContentType());
364                            attach.setAttachmentContent(attachmentFromBusinessObject.getFileData());
365                            attach.setDocumentNumber(getDocumentNumber());
366                            attach.setObjectId(UUID.randomUUID().toString());
367                            attachments.add(attach);
368                        }
369                    } else {
370                        if (persistableAttachment.getFileName() != null
371                                && persistableAttachment.getAttachmentContent() != null) {
372                            MultiDocumentAttachment attach = new MultiDocumentAttachment();
373                            attach.setFileName(persistableAttachment.getFileName());
374                            attach.setContentType(persistableAttachment.getContentType());
375                            attach.setAttachmentContent(persistableAttachment.getAttachmentContent());
376                            attach.setDocumentNumber(getDocumentNumber());
377                            attach.setObjectId(UUID.randomUUID().toString());
378                            //set Bo's content to null
379                            persistableAttachment.setAttachmentContent(null);
380                            attachments.add(attach);
381                        }
382                    }
383                } catch (Exception e) {
384                    LOG.error("Not able to get the attachment " + e.getMessage());
385                    throw new RuntimeException("Not able to get the attachment " + e.getMessage());
386                }
387            }
388
389        }
390    }
391
392    /**
393     * {@inheritDoc}
394     */
395    @Override
396    protected BusinessObjectSerializerService getBusinessObjectSerializerService() {
397        return KRADServiceLocator.getBusinessObjectSerializerService();
398    }
399
400    /**
401     * this needs to happen after the document itself is saved, to preserve consistency of the ver_nbr and in the case
402     * of initial save, because this can't be saved until the document is saved initially
403     *
404     * @see org.kuali.rice.krad.document.DocumentBase#postProcessSave(org.kuali.rice.krad.rules.rule.event.DocumentEvent)
405     */
406    @Override
407    public void postProcessSave(DocumentEvent event) {
408        Object bo = getNewMaintainableObject().getDataObject();
409        if (bo instanceof GlobalBusinessObject) {
410            bo = KRADServiceLocatorWeb.getLegacyDataAdapter().save(bo);
411            // KRAD/JPA - have to change the handle to object to that just saved
412            getNewMaintainableObject().setDataObject(bo);
413        }
414
415        //currently only global documents could change the list of what they're affecting during routing,
416        //so could restrict this to only happening with them, but who knows if that will change, so safest
417        //to always do the delete and re-add...seems a bit inefficient though if nothing has changed, which is
418        //most of the time...could also try to only add/update/delete what's changed, but this is easier
419        if (!(event instanceof SaveDocumentEvent)) { //don't lock until they route
420            getMaintenanceDocumentService().deleteLocks(MaintenanceDocumentBase.this.getDocumentNumber());
421            getMaintenanceDocumentService().storeLocks(MaintenanceDocumentBase.this.getNewMaintainableObject().generateMaintenanceLocks());
422        }
423    }
424
425
426}